第43条:返回零长度的数组或者集合,而不是null
像下面这样的方法并不少见:
private final List<Cheese> cheesesInStock = ...;
/**
* @Return an array containing all of the cheeses in the shop,
* or null if no cheeses are available for purchase.
*/
public Cheese[] getCheeses() {
if (cheesesInStock.size() == 0)
return null;
...
}
把没有奶酪(cheese)可买的情况当做是一种特例,这是不合常理的。这样做会要求客户端中必须有额外的代码来处理null
返回值,例如:
Cheese[] cheeses = shop.getCheeses();
if (cheeses != null &&
Arrays.asList(cheeses).contains(Cheese.STILTON))
System.out.println("Jolly good, just the thing.");
而不是下面这段代码:
if (Arrays.asList(shop.getCheeses()).contains(Cheese.STILTON))
System.out.println("Jolly good, just the thing.");
对于一个返回null
而不是零长度数组或者集合的方法,几乎每次用到该方法时都需要这种曲折的处理方式。这样做很容易出错,因为编写客户端程序的程序猿可能会忘记写这种专门的代码来处理null
返回值。这样的错误也许几年都不会被注意到,因为这样的方法通常返回一个或者多个对象。返回null
而不是零长度的数组也会使返回数组或者集合的方法本身变得更加复杂,这一点虽然不是特别重要,但是也值得注意。
有时候会有人认为:null
返回值比零长度的数组更好,因为它避免了分配数组所需要的开销。这种观点是站不住脚的,原因有两点。第一,在这个级别上担心性能问题是不明智的,除非分析表明这个方法正是造成性能问题的真正源头(见第55条)。第二,对于不返回任何元素的调用,每次都返回同一个零长度数组是有可能的,因为零长度数组是不可变的,而不可变对象有可能被自由地共享(点第15条)。实际上,当你使用标准做法(standard idiom)把一些元素从一个集合转存到一个类型化的数组(typed array)中时,它正是这样做的:
// The right way to return an array from a collection
private final List<Cheese> cheesesInStock = ...;
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
/**
* @return an array containing all of the cheeses in the shop.
*/
public Cheese[] getCheeses() {
return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}
在这种习惯用法中,零长度的数组常量被传递给toArray
方法,以指明所期望的返回类型。正常情况下,toArray
方法分配了返回的数组,但是,如果集合是空的,它将使用零长度的输入数组,Collection.toArray(T[])
的规范保证:如果输入数组大到足够容纳这个集合,它就将返回这个输入数组。因此,这种做法永远也不会分配零长度的数组。
同样地,集合值的方法也可以做成在每当需要返回空集合时都返回同一个不可变的空集合。Collections.emptySet
、emptyList
和emptyMap
方法提供的正是你所需要的。如下所示:
// The right way to return a copy of a collection
public List<Cheese> getCheeseList() {
if (cheesesInStock.isEmpty)
return Collections.emptyList(); // Always returns same list
else
return new ArrayList<Cheese>(cheesesInStock);
}
简而言之,返回类型为数组或集合的方法没理由返回null
,而不是返回一个零长度的数组或者集合。这种习惯做法(指返回null
)很有可能是从C程序设计语言中沿袭过来的,在C语言中,数组长度是与实际的数组分开返回的。在C语言中,如果返回的数组长度为零,再分配一个数组就没有任何好处。