第二个可能令人困惑,因为它暗示正在创建并返回一个新值 - 但事实并非如此。
如果方法是“流式”API 的一部分,则例外情况是,该方法是实例方法并正在修改其实例,然后返回实例以允许方法链接:Java StringBuilder 类是这个例子。
不过,我一般都不会使用。
我会选择您的第三个选项:我编写一个方法来创建并返回一个带有适当更改的新列表。就您的示例而言,这有点人为,因为该示例实际上只是在复制 List.add(),但是...
/** Creates a copy of the list, with val appended. */
public static <T> List<T> modifyMyList(List<T> list, T val) {
List<T> xs = new ArrayList<T>(list);
xs.add(val);
return xs;
}
旁白:我不会,正如 Saket 所建议的那样,返回一个不可变的列表。他关于不变性和并行性的论点是有效的。但大多数情况下,Java 程序员希望能够修改集合,除非在特殊情况下。通过让你的方法返回一个不可变的集合,你将它的可重用性限制在这种情况下。 (如果他们愿意,调用者总是可以使列表不可变:他们知道返回的值是一个副本,不会被其他任何东西触及。)换句话说:Java 不是 Clojure。此外,如果需要考虑并行性,请查看 Java 8 和流(新类型 - 不是 I/O 流)。
这是一个不同的例子:
/** Returns a copy of a list sans-nulls. */
public static <T> List<T> compact(Iterable<T> it) {
List<T> xs = new ArrayList<T>();
for(T x : it)
if(x!=null) xs.add(x);
return xs;
}
请注意,我已经对方法进行了通用化,并使其更广泛地适用于获取Iterable 而不是列表。在实际代码中,我有两个重载版本,一个采用Iterable,一个采用Iterator。 (第一个将通过使用可迭代的迭代器调用第二个来实现。)另外,我将其设为静态,因为您的方法没有理由成为实例方法(它不依赖于实例的状态)。
但有时,如果我正在编写库代码,并且不清楚变异实现还是非变异实现更普遍有用,我会同时创建两者。这是一个更完整的例子:
/** Returns a copy of the elements from an Iterable, as a List, sans-nulls. */
public static <T> List<T> compact(Iterable<T> it) {
return compact(it.iterator());
}
public static <T> List<T> compact(Iterator<T> iter) {
List<T> xs = new ArrayList<T>();
while(iter.hasNext()) {
T x = iter.next();
if(x!=null) xs.add(x);
}
return xs;
}
/** In-place, mutating version of compact(). */
public static <T> void compactIn(Iterable<T> it) {
// Note: for a 'fluent' version of this API, have this return 'it'.
compactIn(it.iterator());
}
public static <T> void compactIn(Iterator<T> iter) {
while(iter.hasNext()) {
T x = iter.next();
if(x==null) iter.remove();
}
}
如果这是在真正的 API 中,我会检查参数是否为 null 并抛出 IllegalArgumentException。 (NOT NullPointerException - 虽然它经常用于此目的。NullPointerException 也因其他原因而发生,例如错误代码。IllegalArgumentException 更适合无效参数。)
(Javadoc 也会比实际代码多!)