Tips

《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化。
在这里第一时间翻译成中文版。供大家学习分享之用。
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。但是Java 9 只是一个过渡版本,所以建议安装JDK 10。

Effective Java, Third Edition

47. 优先使用Collection而不是Stream来作为方法的返回类型

许多方法返回元素序列(sequence)。在Java 8之前,通常方法的返回类型是CollectionSetList这些接口;还包括Iterable和数组类型。通常,很容易决定返回哪一种类型。规范(norm)是集合接口。如果该方法仅用于启用for-each循环,或者返回的序列不能实现某些Collection方法(通常是contains(Object)),则使用迭代(Iterable)接口。如果返回的元素是基本类型或有严格的性能要求,则使用数组。在Java 8中,将流(Stream)添加到平台中,这使得为序列返回方法选择适当的返回类型的任务变得非常复杂。

你可能听说过,流现在是返回元素序列的明显的选择,但是正如条目 45所讨论的,流不会使迭代过时:编写好的代码需要明智地结合流和迭代。如果一个API只返回一个流,并且一些用户想用for-each循环遍历返回的序列,那么这些用户肯定会感到不安。这尤其令人沮丧,因为Stream接口在Iterable接口中包含唯一的抽象方法,Stream的方法规范与Iterable兼容。阻止程序员使用for-each循环在流上迭代的唯一原因是Stream无法继承Iterable。

遗憾的是,这个问题没有好的解决方法。 乍一看,似乎可以将方法引用传递给Stream的iterator方法。 结果代码可能有点嘈杂和不透明,但并非不合理:

// Won't compile, due to limitations on Java's type inference  for (ProcessHandle ph : ProcessHandle.allProcesses()::iterator) {      // Process the process  }

不幸的是,如果你试图编译这段代码,会得到一个错误信息:

Test.java:6: error: method reference not expected here  for (ProcessHandle ph : ProcessHandle.allProcesses()::iterator) { 

为了使代码编译,必须将方法引用强制转换为适当参数化的Iterable类型:

// Hideous workaround to iterate over a stream  for  (ProcessHandle ph : (Iterable<ProcessHandle>)ProcessHandle.allProcesses()::iterator)

此代码有效,但在实践中使用它太嘈杂和不透明。 更好的解决方法是使用适配器方法。 JDK没有提供这样的方法,但是使用上面的代码片段中使用的相同技术,很容易编写一个方法。 请注意,在适配器方法中不需要强制转换,因为Java的类型推断在此上下文中能够正常工作:

// Adapter from  Stream<E> to Iterable<E>  public static <E> Iterable<E> iterableOf(Stream<E> stream) {      return stream::iterator;  }

使用此适配器,可以使用for-each语句迭代任何流:

for (ProcessHandle p : iterableOf(ProcessHandle.allProcesses())) {      // Process the process  }

注意,条目 34中的Anagrams程序的流版本使用Files.lines方法读取字典,而迭代版本使用了scanner