像下面的方法并不少見(jiàn):
// Returns null to indicate an empty collection. Don't do this!
private final List<Cheese> cheesesInStock = ...;
/**
* @return a list containing all of the cheeses in the shop,
* or null if no cheeses are available for purchase.
*/
public List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheesesInStock);
}
把沒(méi)有奶酪可買的情況當(dāng)作是一種特例子,這是不合理的始花。這樣做會(huì)要求客戶端中必須有額外的代碼來(lái)處理null返回值,例如:
List<Cheese> cheeses = shop.getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON))
System.out.println("Jolly good, just the thing.");
對(duì)于一個(gè)返回null而不是零長(zhǎng)度數(shù)組或者集合的方法孩锡,幾乎每次用到該方法時(shí)都需要這種曲折的處理方式酷宵。這樣做很容易出錯(cuò),因?yàn)榫帉懣蛻舳顺绦虻某绦騿T可能會(huì)忘記寫這種專門的代碼來(lái)處理null返回值躬窜。這樣的錯(cuò)誤也許幾年都不會(huì)被注意到浇垦,因?yàn)檫@樣的方法通常返回一個(gè)或者多個(gè)對(duì)象。返回null而不是零長(zhǎng)度的容器荣挨,也會(huì)使返回該容器的方法實(shí)現(xiàn)代碼變得更加復(fù)雜男韧。
有時(shí)候會(huì)有人認(rèn)為:null返回值比零長(zhǎng)度集合或者數(shù)組更好朴摊,因?yàn)樗苊饬朔峙淞汩L(zhǎng)度的容器所需要的開銷。這種觀點(diǎn)是站不住腳的此虑,原因有兩點(diǎn)甚纲。第一,在這個(gè)級(jí)別上擔(dān)心性能問(wèn)題是不明智地朦前,除非分析表明這個(gè)方法正是造成性能問(wèn)題的真正源頭(詳見(jiàn)第67條)介杆。第二,不需要分配零長(zhǎng)度的集合或者數(shù)組韭寸,也可以返回它們春哨。下面是返回可能的零長(zhǎng)度集合的一段典型代碼。一般情況下恩伺,這些都是必須的:
//The right way to return a possibly empty collection
public List<Cheese> getCheeses() {
return new ArrayList<>(cheesesInStock);
}
萬(wàn)一有證據(jù)表明分配零長(zhǎng)度的集合損害了程序的性能赴背,可以通過(guò)重復(fù)返回同一個(gè)不可變的零長(zhǎng)度集合,避免了分配的執(zhí)行晶渠,因?yàn)椴豢勺儗?duì)象可以被自由共享(詳見(jiàn)第17條)凰荚。下面的代碼正是這么做的,它使用了Collections.emptyList方法乱陡。如果返回的是集合浇揩,最好使用Collections.emptySet;如果返回的是映射憨颠,最好使用Collections.emptyMap胳徽。但是要記住,這是一個(gè)優(yōu)化爽彤,并且?guī)缀跤貌簧涎痢H绻阏J(rèn)為確實(shí)需要,必須在行動(dòng)前后分別測(cè)試測(cè)量性能适篙,確保這么做確實(shí)是有幫助的:
// Optimization - avoids allocating empty collections
public List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? Collections.emptyList() : new ArrayList<>(cheesesInStock);
}
數(shù)組的情形與集合的情形一樣往核,它永遠(yuǎn)不會(huì)返回null,而是返回零長(zhǎng)度的數(shù)組嚷节。一般來(lái)說(shuō)聂儒,應(yīng)該只返回一個(gè)正確長(zhǎng)度的數(shù)組,這個(gè)長(zhǎng)度可以為零硫痰。注意衩婚,我們將一個(gè)零長(zhǎng)度的數(shù)組傳遞給了toArray方法,以指明所期望的返回類型效斑,即Cheese[]:
//The right way to return a possibly empty array
public Cheese[] getCheeses() {
return cheesesInStock.toArray(new Cheese[0]);
}
如果確信分配零長(zhǎng)度的數(shù)組會(huì)傷害性能非春,可以重復(fù)返回同一個(gè)零長(zhǎng)度的數(shù)組,因?yàn)樗辛汩L(zhǎng)度的數(shù)組都是不可變的:
// Optimization - avoids allocating empty arrays
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
public Cheese[] getCheeses() {
return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}
在優(yōu)化性能的版本中,我們將同一個(gè)零長(zhǎng)度的數(shù)組傳進(jìn)了每一次的toArrya調(diào)用奇昙,每當(dāng)cheesesInStock為空時(shí)护侮,就會(huì)從getCheese返回這個(gè)數(shù)組。千萬(wàn)不要指望通過(guò)預(yù)先分配傳入toArray的數(shù)組來(lái)提升性能储耐。研究表明羊初,這樣只會(huì)適得其反:
// Don’t do this - preallocating the array harms performance!
return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);
簡(jiǎn)而言之,永遠(yuǎn)不要返回null弧岳,而不會(huì)一個(gè)零長(zhǎng)度的數(shù)組或者集合
凳忙。如果返回null,那么會(huì)使API更難使用禽炬,也更容易出錯(cuò)涧卵,而且沒(méi)有任何性能優(yōu)勢(shì)。