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) {
...
}
}