1、重要的语言变化
l 泛型(Generics)
l 增强型循环(foreach)
l 自动封箱(Autoboxing)和解箱(Unboxing)
l 安全类型的Enums
l Varargs
l 静态import
l Annotations
2、泛型(Generics)
(1)问题
l 从集合中获得元素时,必须进行类型转换:
Ø 类型转换是麻烦的
Ø 类型转换是不安全的,可能在运行时发生类型转换失败
l 为什么不能做的更好:告诉编译器集合中元素的类型?
Ø 让编译器加入类型转换功能
Ø 编译器会保证类型转换的成功
(2)过滤集合的例子
// Removes 4-letter words from c; elements must be strings static void expurgate(Collection c) { for (Iterator i = c.iterator(); i.hasNext();) { String s = (String) i.next(); if (s.length() == 4) { i.remove(); } } }
(3)使用泛型
// Removes 4-letter words from c static void expurgate(Collection<String> c) { for (Iterator<String> i = c.iterator(); i.hasNext();) { if (i.next().length() == 4) { i.remove(); } } }
l 更加清晰和安全
l 没有类型转换、额外的括号和临时变量
l 提供编译时的类型检查
(4)泛型不是模板
l 没有膨胀的代码
l 没有可怕的复杂性
l 没有模板元程序
l 简单的提供编译时类型安全性和消除类型转换
3、增强型循环(foreach)
(1)问题
l 遍历集合是麻烦的事
l Iterator通常只有在获取元素时才会用到
l 使用Iterator倾向于错误:
Ø Iterator变量在循环中出现3次
Ø 使你有两次出错的机会
Ø 通常的拷贝粘贴错误
l 为什么不做的更好,如果能让编译器来为你处理Iterator?
(2)通常访问集合元素的例子
void cancelAll(Collection c) { for (Iterator i = c.iterator(); i.hasNext();) { TimerTask tt = (TimerTask) i.next(); tt.cancel(); } }
(3)使用增强循环的例子
void cancelAll(Collection c) { for (Object o : c) { ((TimerTask) o).cancel(); } }
l 更加清晰和安全
l 和Iterator无关
l 不可能使用错误的Iterator
(4)结合泛型的例子
void cancelAll(Collection<TimerTask> c) { for (TimerTask task : c) { task.cancel(); } }
l 更加简洁、清晰和安全
l 代码准确表达它所要做的
(5)对数组同样适合
//Returns the sum of the elements of a int sum(int[] a) { int result = 0; for (int i : a) { result += i; } return result; }
l 消除使用数组索引的错误
l 具有前面所述的优点
(6)灵活的嵌套Iterator
l 通常的例子
List suits = ...; List ranks = ...; List sortedDeck = new ArrayList(); // Broken - throws NoSuchElementException! for (Iterator i = suits.iterator(); i.hasNext();) { for (Iterator j = ranks.iterator(); j.hasNext();) { sortedDeck.add(new Card(i.next(), j.next())); } } // Fixed - a bit ugly for (Iterator i = suits.iterator(); i.hasNext();) { Suit suit = (Suit) i.next(); for (Iterator j = ranks.iterator(); j.hasNext();) { sortedDeck.add(new Card(suit, j.next())); } }
l 使用增强循环简单而灵活
for (Suit suit : suits) { for (Rank rank : ranks) { sortedDeck.add(new Card(suit, rank)); } }
4、自动封箱(Autoboxing)和解箱(Unboxing)
(1)问题
l 不能将int放入集合,必须使用Integer
l 在获取时转换回来又是麻烦的事
l 由编译器来做这些事不是更好吗?
(2)使用通常方法创建一个频率表的例子
public class Freq { private static final Integer ONE = new Integer(1); public static void main(String[] args) { // Maps word (String) to frequency (Integer) Map m = new TreeMap(); for (int i = 0; i < args.length; i++) { Integer freq = (Integer) m.get(args[i]); m.put(args[i], (freq == null ? ONE : new Integer( freq.intValue() + 1))); } System.out.println(m); } }
(2)结合自动封箱、泛型和增强循环的例子
public class Freq { public static void main(String[] args) { Map<String, Integer> m = new TreeMap<String, Integer>(); for (String word : args) { Integer freq = m.get(word); m.put(word, (freq == null ? 1 : freq + 1)); } System.out.println(m); } }
5、安全类型的Enums
(1)标准的int Enum模式
public class Almanac { public static final int SEASON_WINTER = 0; public static final int SEASON_SPRING = 1; public static final int SEASON_SUMMER = 2; public static final int SEASON_FALL = 3; ... // Remainder omitted }
l 缺点:
Ø 不是安全类型
Ø 没有名字空间:必须要有常量前缀
Ø 脆弱性:常量被编译到客户程序中
Ø 打印出的值不提供信息
(2)安全类型的Enum模式的例子
import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.List; public final class Season implements Comparable, Serializable { private final String name; public String toString() { return name; } private Season(String name) { this.name = name; } public static final Season WINTER = new Season("winter"); public static final Season SPRING = new Season("spring"); public static final Season SUMMER = new Season("summer"); public static final Season FALL = new Season("fall"); private static int nextOrdinal = 0; private final int ordinal = nextOrdinal++; public int compareTo(Object o) { return ordinal - ((Season) o).ordinal; } private static final Season[] PRIVATE_VALUES = { WINTER, SPRING, SUMMER, FALL }; public static final List VALUES = Collections.unmodifiableList(Arrays .asList(PRIVATE_VALUES)); private Object readResolve() { // Canonicalize return PRIVATE_VALUES[ordinal]; } }
l 基本想法:使用导出自定义类型的常量,不提供public构造方法
l 修正上面所有的缺点
l 其它优点:
Ø 能够添加任意的方法、域变量
Ø 能够实现接口
l 缺点:
Ø 代码冗长
Ø 容易出错:每个常量出现3次
Ø 不能在switch语句中使用
l 为什么不能做的更好,由编译器来处理?
(3)安全类型的Enum结构
l 编译器支持安全类型的Enum模式
l 类似典型的Enum(就象C、C++)
Ø enum Season { WINTER, SPRING, SUMMER, FALL }
l 更大强大:
Ø 安全类型的Enum模式的所有优点
Ø 能够在switch语句中使用
(4)结合泛型和增强循环的Enum例子
enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES } enum Rank { DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE } List<Card> deck = new ArrayList<Card>(); for (Suit suit : Suit.values()) { for (Rank rank : Rank.values()) { deck.add(new Card(suit, rank)); } } Collections.shuffle(deck);
(5)有域变量、方法和构造方法的Enum例子
public enum Coin { PENNY(1), NICKEL(5), DIME(10), QUARTER(25); Coin(int value) { this.value = value; } private final int value; public int value() { return value; } }
l 使用Coin的例子:
public class CoinTest { public static void main(String[] args) { for (Coin c : Coin.values()) { System.out.println(c + ": \t" + c.value() + "¢ \t" + color(c)); } } private enum CoinColor { COPPER, NICKEL, SILVER } private static CoinColor color(Coin c) { switch (c) { case PENNY: return CoinColor.COPPER; case NICKEL: return CoinColor.NICKEL; case DIME: case QUARTER: return CoinColor.SILVER; default: throw new AssertionError("Unknown coin: " + c); } } }
6、Varargs
(1)问题
l 编写具有任意数量参数的方法,必须使用数组
l 创建和初始化数组是麻烦的事
l 如果由编译器来实现不是更好?
l 就象printf的基本用法一样
(2)使用java.text.MessageFormat的例子
Object[] arguments = { new Integer(7), new Date(), "a disturbance in the Force" }; String result = MessageFormat.format( "At {1,time} on {1,date}, there was {2} on planet " + "{0,number,integer}.", arguments);
(3)使用Varargs的例子
String result = MessageFormat.format( At {1,time} on {1,date}, there was {2} on planet " + "{0,number,integer}.", 7, new Date(), "a disturbance in the Force");
l format方法的Varargs声明如下:
public static String format(String pattern, Object... arguments)
l 参数类型是Object[]
l 调用者不需要使用Varargs语法
7、静态import
(1)使用类导出常量的例子
public class Physics { public static final double AVOGADROS_NUMBER = 6.02214199e23; public static final double BOLTZMANN_CONSTANT = 1.3806503e-23; public static final double ELECTRON_MASS = 9.10938188e-31; }
l 客户程序需要使用限定来访问常量:
double molecules = Physics.AVOGADROS_NUMBER * moles;
(2)避免限定的错误方法
// "Constant Interface" antipattern - do not use! public interface Physics { public static final double AVOGADROS_NUMBER = 6.02214199e23; public static final double BOLTZMANN_CONSTANT = 1.3806503e-23; public static final double ELECTRON_MASS = 9.10938188e-31; } public class Guacamole implements Physics { public static void main(String[] args) { double moles = ...; double molecules = AVOGADROS_NUMBER * moles; ... }
l 存在的问题:
Ø 滥用接口:不是用来定义类型的
Ø 实现细节污染导出API
Ø 使客户程序混乱
Ø 创建长期的承诺
Ø 如果编译器让我们避免限定名字不是更好吗?
(3)解决:静态import
l 类似包的导入
l 导入类的静态成员
l 可以单个也可以全部导入
l 使用静态import的例子:
import static org.iso.Physics.*; public class Guacamole { public static void main(String[] args) { double molecules = AVOGADROS_NUMBER * moles; ... } }
(4)导入方法的例子(Math类)
l 替代
x = Math.cos(Math.PI * theta);
成:
x = cos(Math.PI * theta);
(5)和Enum一起工作
import static gov.treas.Coin.*; class MyClass { public static void main(String[] args) { int twoBits = 2 * QUARTER.value(); ... } }
8、元数据(Annotations)
(1)问题
l 许多API需要相当数量的样板文件,如JAX-RPC Web服务需要成对的接口和实现
l 如果能够注视代码,使得工具能够生成样板文件不是更好?
l 许多API需要附加的文件来进行维护,如Bean需要BeanInfo类文件
l 如果能够注视代码,使得工具能够生成这些附加文件不是更好?
(2)JAX-RPC Web服务的例子
public interface CoffeeOrderIF extends java.rmi.Remote { public Coffee [] getPriceList() throws java.rmi.RemoteException; public String orderCoffee(String name, int quantity) throws java.rmi.RemoteException; } public class CoffeeOrderImpl implements CoffeeOrderIF { public Coffee [] getPriceList() { ... } public String orderCoffee(String name, int quantity) { ... } }
(3)使用Annotations的例子
import javax.xml.rpc.*; public class CoffeeOrder { @Remote public Coffee [] getPriceList() { ... } @Remote public String orderCoffee(String name, int quantity) { ... } }

|