## Chapter 7. Lambdas and Streams(λ 表達式和流)
### Item 42: Prefer lambdas to anonymous classes(λ 表達式優于匿名類)
Historically, interfaces (or, rarely, abstract classes) with a single abstract method were used as function types. Their instances, known as function objects, represent functions or actions. Since JDK 1.1 was released in 1997, the primary means of creating a function object was the anonymous class (Item 24). Here’s a code snippet to sort a list of strings in order of length, using an anonymous class to create the sort’s comparison function (which imposes the sort order):
在歷史上,帶有單個抽象方法的接口(或者抽象類,但這種情況很少)被用作函數類型。它們的實例(稱為函數對象)表示函數或操作。自從 JDK 1.1 在 1997 年發布以來,創建函數對象的主要方法就是匿名類([Item-24](/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。下面是一個按長度對字符串列表進行排序的代碼片段,使用一個匿名類來創建排序的比較函數(它強制執行排序順序):
```
// Anonymous class instance as a function object - obsolete!
Collections.sort(words, new Comparator<String>() {
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});
```
Anonymous classes were adequate for the classic objected-oriented design patterns requiring function objects, notably the Strategy pattern [Gamma95]. The Comparator interface represents an abstract strategy for sorting; the anonymous class above is a concrete strategy for sorting strings. The verbosity of anonymous classes, however, made functional programming in Java an unappealing prospect.
匿名類對于需要函數對象的典型面向對象設計模式來說已經足夠了,尤其是策略模式 [Gamma95]。Comparator 接口表示排序的抽象策略;上述匿名類是對字符串排序的一種具體策略。然而,匿名類的冗長使函數式編程在 Java 中變得毫無吸引力。
In Java 8, the language formalized the notion that interfaces with a single abstract method are special and deserve special treatment. These interfaces are now known as functional interfaces, and the language allows you to create instances of these interfaces using lambda expressions, or lambdas for short. Lambdas are similar in function to anonymous classes, but far more concise. Here’s how the code snippet above looks with the anonymous class replaced by a lambda. The boilerplate is gone, and the behavior is clearly evident:
在 Java 8 中官方化了一個概念,即具有單個抽象方法的接口是特殊的,應該得到特殊處理。這些接口現在被稱為函數式接口,允許使用 lambda 表達式創建這些接口的實例。Lambda 表達式在功能上類似于匿名類,但是更加簡潔。下面的代碼片段,匿名類被 lambda 表達式替換。已經沒有了原有刻板的樣子,意圖非常明顯:
```
// Lambda expression as function object (replaces anonymous class)
Collections.sort(words,(s1, s2) -> Integer.compare(s1.length(), s2.length()));
```
Note that the types of the lambda (`Comparator<String>`), of its parameters (s1 and s2, both String), and of its return value (int) are not present in the code. The compiler deduces these types from context, using a process known as type inference. In some cases, the compiler won’t be able to determine the types, and you’ll have to specify them. The rules for type inference are complex: they take up an entire chapter in the JLS [JLS, 18]. Few programmers understand these rules in detail, but that’s OK. **Omit the types of all lambda parameters unless their presence makes your program clearer.** If the compiler generates an error telling you it can’t infer the type of a lambda parameter, then specify it. Sometimes you may have to cast the return value or the entire lambda expression, but this is rare.
注意,lambda 表達式(`Comparator<String>`)、它的參數(s1 和 s2,都是字符串)及其返回值(int)的類型在代碼中不存在。編譯器使用稱為類型推斷的過程從上下文中推斷這些類型。在某些情況下,編譯器無法確定類型,你必須顯式指定它們。類型推斷的規則很復雜:它們在 JLS 中占了整整一章 [JLS, 18]。很少有程序員能詳細理解這些規則,但這沒有關系。**省略所有 lambda 表達式參數的類型,除非它們的存在使你的程序更清晰。** 如果編譯器生成一個錯誤,告訴你它不能推斷 lambda 表達式參數的類型,那么就顯式指定它。有時你可能必須強制轉換返回值或整個 lambda 表達式,但這種情況很少見。
One caveat should be added concerning type inference. Item 26 tells you not to use raw types, Item 29 tells you to favor generic types, and Item 30 tells you to favor generic methods. This advice is doubly important when you’re using lambdas, because the compiler obtains most of the type information that allows it to perform type inference from generics. If you don’t provide this information, the compiler will be unable to do type inference, and you’ll have to specify types manually in your lambdas, which will greatly increase their verbosity. By way of example, the code snippet above won’t compile if the variable words is declared to be of the raw type List instead of the parameterized type `List<String>`.
關于類型推斷,有些警告應該被提及。[Item-26](/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md) 告訴你不要使用原始類型,[Item-29](/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md) 告訴你要優先使用泛型,[Item-30](/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md) 告訴你要優先使用泛型方法。在使用 lambda 表達式時,這些建議尤其重要,因為編譯器獲得了允許它從泛型中執行類型推斷的大部分類型信息。如果不提供此信息,編譯器將無法進行類型推斷,并且必須在 lambda 表達式中手動指定類型,這將大大增加它們的冗長。舉例來說,如果變量聲明為原始類型 List 而不是參數化類型 `List<String>`,那么上面的代碼片段將無法編譯。
Incidentally, the comparator in the snippet can be made even more succinct if a comparator construction method is used in place of a lambda (Items 14. 43):
順便說一下,如果使用 comparator 構造方法代替 lambda 表達式([Item-14](/Chapter-3/Chapter-3-Item-14-Consider-implementing-Comparable.md)),那么代碼片段可以變得更加簡潔:
```
Collections.sort(words, comparingInt(String::length));
```
In fact, the snippet can be made still shorter by taking advantage of the sort method that was added to the List interface in Java 8:
事實上,通過 Java 8 中添加到 List 接口的 sort 方法,可以使代碼片段變得更短:
```
words.sort(comparingInt(String::length));
```
The addition of lambdas to the language makes it practical to use function objects where it would not previously have made sense. For example, consider the Operation enum type in Item 34. Because each enum required different behavior for its apply method, we used constant-specific class bodies and overrode the apply method in each enum constant. To refresh your memory, here is the code:
在語言中添加 lambda 表達式使得在以前沒有意義的地方使用函數對象變得實際。例如,考慮 [Item-34](/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md) 中的操作枚舉類型。因為每個枚舉的 apply 方法需要不同的行為,所以我們使用特定于常量的類體并覆蓋每個枚舉常量中的 apply 方法。為了喚醒你的記憶,以下是代碼:
```
// Enum type with constant-specific class bodies & data (Item 34)
public enum Operation {
PLUS("+") {
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
public double apply(double x, double y) { return x / y; }
};
private final String symbol;
Operation(String symbol) { this.symbol = symbol; }
@Override
public String toString() { return symbol; }
public abstract double apply(double x, double y);
}
```
Item 34 says that enum instance fields are preferable to constant-specific class bodies. Lambdas make it easy to implement constant-specific behavior using the former instead of the latter. Merely pass a lambda implementing each enum constant’s behavior to its constructor. The constructor stores the lambda in an instance field, and the apply method forwards invocations to the lambda. The resulting code is simpler and clearer than the original version:
[Item-34](/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md) 指出,枚舉實例字段比特定于常量的類體更可取。Lambda 表達式使得使用前者取代后者來實現特定于常量的行為變得容易。只需將實現每個枚舉常量的行為的 lambda 表達式傳遞給它的構造函數。構造函數將 lambda 表達式存儲在實例字段中,apply 方法將調用轉發給 lambda 表達式。生成的代碼比原始版本更簡單、更清晰:
```
// Enum with function object fields & constant-specific behavior
public enum Operation {
PLUS ("+", (x, y) -> x + y),
MINUS ("-", (x, y) -> x - y),
TIMES ("*", (x, y) -> x * y),
DIVIDE("/", (x, y) -> x / y);
private final String symbol;
private final DoubleBinaryOperator op;
Operation(String symbol, DoubleBinaryOperator op) {
this.symbol = symbol;
this.op = op;
}
@Override public String toString() { return symbol; }
public double apply(double x, double y) {
return op.applyAsDouble(x, y);
}
}
```
Note that we’re using the DoubleBinaryOperator interface for the lambdas that represent the enum constant’s behavior. This is one of the many predefined functional interfaces in java.util.function (Item 44). It represents a function that takes two double arguments and returns a double result.
注意,我們對表示枚舉常量行為的 lambda 表達式使用了 DoubleBinaryOperator 接口。這是 `java.util.function` ([Item-44](/Chapter-7/Chapter-7-Item-44-Favor-the-use-of-standard-functional-interfaces.md))中許多預定義的函數式接口之一。它表示一個函數,該函數接收兩個 double 類型參數,并且返回值也為 double 類型。
Looking at the lambda-based Operation enum, you might think constantspecific method bodies have outlived their usefulness, but this is not the case. Unlike methods and classes, **lambdas lack names and documentation; if a computation isn’t self-explanatory, or exceeds a few lines, don’t put it in a lambda.** One line is ideal for a lambda, and three lines is a reasonable maximum. If you violate this rule, it can cause serious harm to the readability of your programs. If a lambda is long or difficult to read, either find a way to simplify it or refactor your program to eliminate it. Also, the arguments passed to enum constructors are evaluated in a static context. Thus, lambdas in enum constructors can’t access instance members of the enum. Constant-specific class bodies are still the way to go if an enum type has constant-specific behavior that is difficult to understand, that can’t be implemented in a few lines, or that requires access to instance fields or methods.
查看基于 lambda 表達式的操作 enum,你可能會認為特定于常量的方法體已經過時了,但事實并非如此。與方法和類不同,**lambda 表達式缺少名稱和文檔;如果一個算法并非不言自明,或者有很多行代碼,不要把它放在 lambda 表達式中。** 一行是理想的,三行是合理的最大值。如果你違反了這一規則,就會嚴重損害程序的可讀性。如果 lambda 表達式很長或者很難讀,要么找到一種方法來簡化它,要么重構你的程序。此外,傳遞給 enum 構造函數的參數在靜態上下文中計算。因此,enum 構造函數中的 lambda 表達式不能訪問枚舉的實例成員。如果枚舉類型具有難以理解的特定于常量的行為,無法在幾行代碼中實現,或者需要訪問實例字段或方法,則仍然需要特定于常量的類。
Likewise, you might think that anonymous classes are obsolete in the era of lambdas. This is closer to the truth, but there are a few things you can do with anonymous classes that you can’t do with lambdas. Lambdas are limited to functional interfaces. If you want to create an instance of an abstract class, you can do it with an anonymous class, but not a lambda. Similarly, you can use anonymous classes to create instances of interfaces with multiple abstract methods. Finally, a lambda cannot obtain a reference to itself. In a lambda, the this keyword refers to the enclosing instance, which is typically what you want. In an anonymous class, the this keyword refers to the anonymous class instance. If you need access to the function object from within its body, then you must use an anonymous class.
同樣,你可能認為匿名類在 lambda 表達式時代已經過時了。這更接近事實,但是有一些匿名類可以做的事情是 lambda 表達式不能做的。Lambda 表達式僅限于函數式接口。如果想創建抽象類的實例,可以使用匿名類,但不能使用 lambda 表達式。類似地,你可以使用匿名類來創建具有多個抽象方法的接口實例。最后,lambda 表達式無法獲得對自身的引用。在 lambda 表達式中,this 關鍵字指的是封閉實例,這通常是你想要的。在匿名類中,this 關鍵字引用匿名類實例。如果你需要從函數對象的內部訪問它,那么你必須使用一個匿名類。
Lambdas share with anonymous classes the property that you can’t reliably serialize and deserialize them across implementations. Therefore, **you should rarely, if ever, serialize a lambda** (or an anonymous class instance). If you have a function object that you want to make serializable, such as a Comparator, use an instance of a private static nested class (Item 24).
Lambda 表達式與匿名類共享無法通過實現可靠地序列化和反序列化它們的屬性。因此,**很少(如果有的話)序列化 lambda**(或匿名類實例)。如果你有一個想要序列化的函數對象,比如比較器,那么使用私有靜態嵌套類的實例([Item-24](/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。
In summary, as of Java 8, lambdas are by far the best way to represent small function objects. **Don’t use anonymous classes for function objects unless you have to create instances of types that aren’t functional interfaces.** Also, remember that lambdas make it so easy to represent small function objects that it opens the door to functional programming techniques that were not previously practical in Java.
總之,在 Java 8 中,lambda 表達式是迄今為止表示小函數對象的最佳方式。**不要對函數對象使用匿名類,除非你必須創建非函數式接口類型的實例。** 另外,請記住,lambda 表達式使表示小函數對象變得非常容易,從而為 Java 以前不實用的函數式編程技術打開了大門。
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-7/Chapter-7-Introduction.md)**
- **Next Item(下一條目):[Item 43: Prefer method references to lambdas(方法引用優于 λ 表達式)](/Chapter-7/Chapter-7-Item-43-Prefer-method-references-to-lambdas.md)**
- Chapter 2. Creating and Destroying Objects(創建和銷毀對象)
- Item 1: Consider static factory methods instead of constructors(考慮以靜態工廠方法代替構造函數)
- Item 2: Consider a builder when faced with many constructor parameters(在面對多個構造函數參數時,請考慮構建器)
- Item 3: Enforce the singleton property with a private constructor or an enum type(使用私有構造函數或枚舉類型實施單例屬性)
- Item 4: Enforce noninstantiability with a private constructor(用私有構造函數實施不可實例化)
- Item 5: Prefer dependency injection to hardwiring resources(依賴注入優于硬連接資源)
- Item 6: Avoid creating unnecessary objects(避免創建不必要的對象)
- Item 7: Eliminate obsolete object references(排除過時的對象引用)
- Item 8: Avoid finalizers and cleaners(避免使用終結器和清除器)
- Item 9: Prefer try with resources to try finally(使用 try-with-resources 優于 try-finally)
- Chapter 3. Methods Common to All Objects(對象的通用方法)
- Item 10: Obey the general contract when overriding equals(覆蓋 equals 方法時應遵守的約定)
- Item 11: Always override hashCode when you override equals(當覆蓋 equals 方法時,總要覆蓋 hashCode 方法)
- Item 12: Always override toString(始終覆蓋 toString 方法)
- Item 13: Override clone judiciously(明智地覆蓋 clone 方法)
- Item 14: Consider implementing Comparable(考慮實現 Comparable 接口)
- Chapter 4. Classes and Interfaces(類和接口)
- Item 15: Minimize the accessibility of classes and members(盡量減少類和成員的可訪問性)
- Item 16: In public classes use accessor methods not public fields(在公共類中,使用訪問器方法,而不是公共字段)
- Item 17: Minimize mutability(減少可變性)
- Item 18: Favor composition over inheritance(優先選擇復合而不是繼承)
- Item 19: Design and document for inheritance or else prohibit it(繼承要設計良好并且具有文檔,否則禁止使用)
- Item 20: Prefer interfaces to abstract classes(接口優于抽象類)
- Item 21: Design interfaces for posterity(為后代設計接口)
- Item 22: Use interfaces only to define types(接口只用于定義類型)
- Item 23: Prefer class hierarchies to tagged classes(類層次結構優于帶標簽的類)
- Item 24: Favor static member classes over nonstatic(靜態成員類優于非靜態成員類)
- Item 25: Limit source files to a single top level class(源文件僅限有單個頂層類)
- Chapter 5. Generics(泛型)
- Item 26: Do not use raw types(不要使用原始類型)
- Item 27: Eliminate unchecked warnings(消除 unchecked 警告)
- Item 28: Prefer lists to arrays(list 優于數組)
- Item 29: Favor generic types(優先使用泛型)
- Item 30: Favor generic methods(優先使用泛型方法)
- Item 31: Use bounded wildcards to increase API flexibility(使用有界通配符增加 API 的靈活性)
- Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數)
- Item 33: Consider typesafe heterogeneous containers(考慮類型安全的異構容器)
- Chapter 6. Enums and Annotations(枚舉和注解)
- Item 34: Use enums instead of int constants(用枚舉類型代替 int 常量)
- Item 35: Use instance fields instead of ordinals(使用實例字段替代序數)
- Item 36: Use EnumSet instead of bit fields(用 EnumSet 替代位字段)
- Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數索引)
- Item 38: Emulate extensible enums with interfaces(使用接口模擬可擴展枚舉)
- Item 39: Prefer annotations to naming patterns(注解優于命名模式)
- Item 40: Consistently use the Override annotation(堅持使用 @Override 注解)
- Item 41: Use marker interfaces to define types(使用標記接口定義類型)
- Chapter 7. Lambdas and Streams(λ 表達式和流)
- Item 42: Prefer lambdas to anonymous classes(λ 表達式優于匿名類)
- Item 43: Prefer method references to lambdas(方法引用優于 λ 表達式)
- Item 44: Favor the use of standard functional interfaces(優先使用標準函數式接口)
- Item 45: Use streams judiciously(明智地使用流)
- Item 46: Prefer side effect free functions in streams(在流中使用無副作用的函數)
- Item 47: Prefer Collection to Stream as a return type(優先選擇 Collection 而不是流作為返回類型)
- Item 48: Use caution when making streams parallel(謹慎使用并行流)
- Chapter 8. Methods(方法)
- Item 49: Check parameters for validity(檢查參數的有效性)
- Item 50: Make defensive copies when needed(在需要時制作防御性副本)
- Item 51: Design method signatures carefully(仔細設計方法簽名)
- Item 52: Use overloading judiciously(明智地使用重載)
- Item 53: Use varargs judiciously(明智地使用可變參數)
- Item 54: Return empty collections or arrays, not nulls(返回空集合或數組,而不是 null)
- Item 55: Return optionals judiciously(明智地的返回 Optional)
- Item 56: Write doc comments for all exposed API elements(為所有公開的 API 元素編寫文檔注釋)
- Chapter 9. General Programming(通用程序設計)
- Item 57: Minimize the scope of local variables(將局部變量的作用域最小化)
- Item 58: Prefer for-each loops to traditional for loops(for-each 循環優于傳統的 for 循環)
- Item 59: Know and use the libraries(了解并使用庫)
- Item 60: Avoid float and double if exact answers are required(若需要精確答案就應避免使用 float 和 double 類型)
- Item 61: Prefer primitive types to boxed primitives(基本數據類型優于包裝類)
- Item 62: Avoid strings where other types are more appropriate(其他類型更合適時應避免使用字符串)
- Item 63: Beware the performance of string concatenation(當心字符串連接引起的性能問題)
- Item 64: Refer to objects by their interfaces(通過接口引用對象)
- Item 65: Prefer interfaces to reflection(接口優于反射)
- Item 66: Use native methods judiciously(明智地使用本地方法)
- Item 67: Optimize judiciously(明智地進行優化)
- Item 68: Adhere to generally accepted naming conventions(遵守被廣泛認可的命名約定)
- Chapter 10. Exceptions(異常)
- Item 69: Use exceptions only for exceptional conditions(僅在確有異常條件下使用異常)
- Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors(對可恢復情況使用 checked 異常,對編程錯誤使用運行時異常)
- Item 71: Avoid unnecessary use of checked exceptions(避免不必要地使用 checked 異常)
- Item 72: Favor the use of standard exceptions(鼓勵復用標準異常)
- Item 73: Throw exceptions appropriate to the abstraction(拋出能用抽象解釋的異常)
- Item 74: Document all exceptions thrown by each method(為每個方法記錄會拋出的所有異常)
- Item 75: Include failure capture information in detail messages(異常詳細消息中應包含捕獲失敗的信息)
- Item 76: Strive for failure atomicity(盡力保證故障原子性)
- Item 77: Don’t ignore exceptions(不要忽略異常)
- Chapter 11. Concurrency(并發)
- Item 78: Synchronize access to shared mutable data(對共享可變數據的同步訪問)
- Item 79: Avoid excessive synchronization(避免過度同步)
- Item 80: Prefer executors, tasks, and streams to threads(Executor、task、流優于直接使用線程)
- Item 81: Prefer concurrency utilities to wait and notify(并發實用工具優于 wait 和 notify)
- Item 82: Document thread safety(文檔應包含線程安全屬性)
- Item 83: Use lazy initialization judiciously(明智地使用延遲初始化)
- Item 84: Don’t depend on the thread scheduler(不要依賴線程調度器)
- Chapter 12. Serialization(序列化)
- Item 85: Prefer alternatives to Java serialization(優先選擇 Java 序列化的替代方案)
- Item 86: Implement Serializable with great caution(非常謹慎地實現 Serializable)
- Item 87: Consider using a custom serialized form(考慮使用自定義序列化形式)
- Item 88: Write readObject methods defensively(防御性地編寫 readObject 方法)
- Item 89: For instance control, prefer enum types to readResolve(對于實例控制,枚舉類型優于 readResolve)
- Item 90: Consider serialization proxies instead of serialized instances(考慮以序列化代理代替序列化實例)