1、重要的語言變化
l 泛型(Generics)
l 增強(qiáng)型循環(huán)(foreach)
l 自動封箱(Autoboxing)和解箱(Unboxing)
l 安全類型的Enums
l Varargs
l 靜態(tài)import
l Annotations
2、泛型(Generics)
(1)問題
l 從集合中獲得元素時,必須進(jìn)行類型轉(zhuǎn)換:
? 類型轉(zhuǎn)換是麻煩的
? 類型轉(zhuǎn)換是不安全的,可能在運(yùn)行時發(fā)生類型轉(zhuǎn)換失敗
l 為什么不能做的更好:告訴編譯器集合中元素的類型?
? 讓編譯器加入類型轉(zhuǎn)換功能
? 編譯器會保證類型轉(zhuǎn)換的成功
(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 沒有類型轉(zhuǎn)換、額外的括號和臨時變量
l 提供編譯時的類型檢查
(4)泛型不是模板
l 沒有膨脹的代碼
l 沒有可怕的復(fù)雜性
l 沒有模板元程序
l 簡單的提供編譯時類型安全性和消除類型轉(zhuǎn)換
3、增強(qiáng)型循環(huán)(foreach)
(1)問題
l 遍歷集合是麻煩的事
l Iterator通常只有在獲取元素時才會用到
l 使用Iterator傾向于錯誤:
? Iterator變量在循環(huán)中出現(xiàn)3次
? 使你有兩次出錯的機(jī)會
? 通常的拷貝粘貼錯誤
l 為什么不做的更好,如果能讓編譯器來為你處理Iterator?
(2)通常訪問集合元素的例子
void cancelAll(Collection c) {
for (Iterator i = c.iterator(); i.hasNext();) {
TimerTask tt = (TimerTask) i.next();
tt.cancel();
}
}
(3)使用增強(qiáng)循環(huán)的例子
void cancelAll(Collection c) {
for (Object o : c) {
((TimerTask) o).cancel();
}
}
l 更加清晰和安全
l 和Iterator無關(guān)
l 不可能使用錯誤的Iterator
(4)結(jié)合泛型的例子
void cancelAll(Collection<TimerTask> c) {
for (TimerTask task : c) {
task.cancel();
}
}
l 更加簡潔、清晰和安全
l 代碼準(zhǔn)確表達(dá)它所要做的
(5)對數(shù)組同樣適合
//Returns the sum of the elements of a
int sum(int[] a) {
int result = 0;
for (int i : a) {
result += i;
}
return result;
}
l 消除使用數(shù)組索引的錯誤
l 具有前面所述的優(yōu)點(diǎn)
(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 使用增強(qiáng)循環(huán)簡單而靈活
for (Suit suit : suits) {
for (Rank rank : ranks) {
sortedDeck.add(new Card(suit, rank));
}
}
4、自動封箱(Autoboxing)和解箱(Unboxing)
(1)問題
l 不能將int放入集合,必須使用Integer
l 在獲取時轉(zhuǎn)換回來又是麻煩的事
l 由編譯器來做這些事不是更好嗎?
(2)使用通常方法創(chuàng)建一個頻率表的例子
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)結(jié)合自動封箱、泛型和增強(qiáng)循環(huán)的例子
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)標(biāo)準(zhǔn)的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 缺點(diǎn):
? 不是安全類型
? 沒有名字空間:必須要有常量前綴
? 脆弱性:常量被編譯到客戶程序中
? 打印出的值不提供信息
(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 基本想法:使用導(dǎo)出自定義類型的常量,不提供public構(gòu)造方法
l 修正上面所有的缺點(diǎn)
l 其它優(yōu)點(diǎn):
? 能夠添加任意的方法、域變量
? 能夠?qū)崿F(xiàn)接口
l 缺點(diǎn):
? 代碼冗長
? 容易出錯:每個常量出現(xiàn)3次
? 不能在switch語句中使用
l 為什么不能做的更好,由編譯器來處理?
(3)安全類型的Enum結(jié)構(gòu)
l 編譯器支持安全類型的Enum模式
l 類似典型的Enum(就象C、C++)
? enum Season { WINTER, SPRING, SUMMER, FALL }
l 更大強(qiáng)大:
? 安全類型的Enum模式的所有優(yōu)點(diǎn)
? 能夠在switch語句中使用
(4)結(jié)合泛型和增強(qiáng)循環(huán)的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)有域變量、方法和構(gòu)造方法的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 編寫具有任意數(shù)量參數(shù)的方法,必須使用數(shù)組
l 創(chuàng)建和初始化數(shù)組是麻煩的事
l 如果由編譯器來實(shí)現(xiàn)不是更好?
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 參數(shù)類型是Object[]
l 調(diào)用者不需要使用Varargs語法
7、靜態(tài)import
(1)使用類導(dǎo)出常量的例子
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 存在的問題:
? 濫用接口:不是用來定義類型的
? 實(shí)現(xiàn)細(xì)節(jié)污染導(dǎo)出API
? 使客戶程序混亂
? 創(chuàng)建長期的承諾
? 如果編譯器讓我們避免限定名字不是更好嗎?
(3)解決:靜態(tài)import
l 類似包的導(dǎo)入
l 導(dǎo)入類的靜態(tài)成員
l 可以單個也可以全部導(dǎo)入
l 使用靜態(tài)import的例子:
import static org.iso.Physics.*;
public class Guacamole {
public static void main(String[] args) {
double molecules = AVOGADROS_NUMBER * moles;
...
}
}
(4)導(dǎo)入方法的例子(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、元數(shù)據(jù)(Annotations)
(1)問題
l 許多API需要相當(dāng)數(shù)量的樣板文件,如JAX-RPC Web服務(wù)需要成對的接口和實(shí)現(xiàn)
l 如果能夠注視代碼,使得工具能夠生成樣板文件不是更好?
l 許多API需要附加的文件來進(jìn)行維護(hù),如Bean需要BeanInfo類文件
l 如果能夠注視代碼,使得工具能夠生成這些附加文件不是更好?
(2)JAX-RPC Web服務(wù)的例子
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) {
...
}
}