54. 返回空的数组或集合,不要返回 null

      把没有奶酪(Cheese)可买的情况当做一种特例,这是不合常理的。这样需要在客户端中必须有额外的代码来处理 null 的返回值,如:

    1. if (cheeses != null && cheeses.contains(Cheese.STILTON))
    2. System.out.println("Jolly good, just the thing.");

      在几乎每次使用返回 null 来代替空集合或数组的方法时,都需要使用这种迂回的方式。 这样做很容易出错,因为编写客户端的程序员可能忘记编写特殊情况代码来处理 null 返回。 多年来这种错误可能会被忽视,因为这种方法通常会返回一个或多个对象。 此外,返回 null 代替空容器会使返回容器的方法的实现变得复杂。

      如果有证据表明分配空集合会损害性能,可以通过重复返回相同的不可变空集合来避免分配,因为不可变对象可以自由共享(详见第 17 条)。下面的代码就是这样做的,使用了 Collections.emptyList 方法。如果你要返回一个 Set,可以使用 Collections.emptySet ;如果要返回 Map,则使用 Collections.emptyMap。但是请记住,这是一个优化,很少需要它。如果你认为你需要它,测量一下前后的性能表现,确保它确实有帮助:

    1. // Optimization - avoids allocating empty collections
    2. return cheesesInStock.isEmpty() ? Collections.emptyList()
    3. : new ArrayList<>(cheesesInStock);
    4. }

      数组的情况与集合的情况相同。 永远不要返回 null,而是返回长度为零的数组。 通常,应该只返回一个正确长度的数组,这个长度可能为零。 请注意,我们将一个长度为零的数组传递给 toArray 方法,以指示所需的返回类型,即 Cheese []

    1. // Optimization - avoids allocating empty arrays
    2. public Cheese[] getCheeses() {
    3. return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
    4. }

      在优化的版本中,我们将相同的空数组传递到每个 toArray 调用中,当 cheesesInStock 为空时,这个数组将从 返回。不要为了提高性能而预先分配传递给 toArray 的数组。研究表明,这样做会适得其反[Shipilev16]:

      总之,永远不要返回 null 来代替空数组或集合。它使你的 API 更难以使用,更容易出错,并且没有性能优势。