Tips
"Effective Java, third Edition" an English version has been published, the second edition of this book presumably many people have read, known as one of the four major Java books, but the second edition of 2009 published, to now nearly 8 years, but with Java 6, 7, 8, and even 9 of the release, the Java language has undergone profound changes.
In the first time here translated into Chinese version. For everyone to learn to share.
42.lambda expressions better than anonymous classes
In Java 8, functional interfaces, lambda expressions, and method references were added to make it easier to create function objects. The Stream API is added in conjunction with other language modifications to provide class library support for processing data element sequences. In this chapter, we will discuss how to make the most of these features.
Historically, interfaces using a single abstract method (or rarely using abstract classes) were used as function types. Their instances (called function objects) represent functions (functions) or actions (actions). Since JDK 1.1 was released in 1997, the primary means of creating function objects is anonymous classes (entry 24). Here is a snippet of code that sorts the list in string-length order, using anonymous classes to create a sort comparison method (Force sort order):
// 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 are intended for classic object-oriented design patterns that require function objects, especially the policy mode [GAMMA95]. The comparator interface represents an abstract strategy for sorting; The above anonymous class is a specific strategy for sorting strings. However, the verbosity of anonymous classes makes functional programming in Java an attractive prospect.
In Java 8, the language forms the notion that an interface using a single abstract method is special and deserves special treatment. These interfaces are now called functional interfaces, and the language allows you to create instances of these interfaces using lambda expressions or simply lambda. Lambdas is functionally similar to anonymous classes, but more concise. The following code replaces the above anonymous class with Lambdas. The template is missing and the behavior is clear:
// Lambda expression as function object (replaces anonymous class)Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
Note that there is no lambda () in the code, Comparator <String>
its arguments (S1 and S2, all string types), and the type of its return value (int). The compiler uses procedures called type inference to derive these types from context. In some cases, the compiler will not be able to determine the type and must specify them. The rules for type inference are complex: they occupy the entire chapter in JLS [jls,18]. Few programmers know more about these rules, but that's okay. Unless they exist to make your program clearer, omit all lambda parameter types. If the compiler generates an error telling you that it cannot infer the type of the lambda parameter, specify it. Sometimes you may have to cast a return value or an entire lambda expression, but this is rare.
There is a point to note about type inference. Entry 26 tells you not to use the original type, entry 29 tells you the preference generic type, and entry 30 tells you to favor the generic method. This recommendation is important when using lambda expressions, because the compiler obtains most of the type information that allows it to be inferred from the generic type. If you do not provide this information, the compiler will not be able to type inference, you must manually specify the type in Lambdas, which will greatly increase their redundancy. For example, if a variable is declared as a list of the original type instead of a parameterized type List <String>
, the above code fragment will not compile.
Incidentally, if you use a comparator construction method instead of a lambda, the comparer in your code can become more concise (entry 14,43):
Collections.sort(words, comparingInt(String::length));
In fact, you can make a fragment shorter by taking advantage of the sort method added to the list interface in Java 8:
words.sort(comparingInt(String::length));
Adding Lambdas to the language makes it useful to use function objects where there was no previous meaning. For example, consider the enumeration type in entry 34 Operation
. Because each enumeration requires a different application behavior, we use a constant-specific class body and override the Apply method in each enumeration constant. In order to refresh your memory, here is the previous code:
// Enum type with constant-specific class bodies & data 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; }[email protected] public String toString() { return symbol; }????public abstract double apply(double x, double y);}
The 34th article says that enumerating instance properties is preferable to a constant-specific class body. Lambdas can easily use the former rather than the latter to implement constant-specific behavior. Only a lambda that implements the behavior of each enumeration constant is passed to its constructor method. The construct method stores the lambda in the instance properties, and the Apply method forwards the call to the lambda. The resulting code is simpler and clearer than the original version:
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;????}[email protected] public String toString() { return symbol; }????public double apply(double x, double y) {????????return op.applyAsDouble(x, y);????}}
Note that we use the Lambdas interface that represents the behavior of enumeration constants DoubleBinaryOperator
. This is java.util.function
one of many of the pre-defined function interfaces (entry 44). It represents a function that accepts two double type arguments and returns the result of a double type.
Looking at Lambda-based Operation
enumerations, you might think that constant-specific method bodies have lost their usefulness, but that's not the case. Unlike methods and classes,Lambda does not have a name and document, and if the calculation is not self-explanatory or more than a few lines, do not put it in a lambda expression . One line of code is ideal for lambda, and three lines of code is a reasonable maximum value. Violation of this provision may seriously impair the readability of the program. If a lambda is long or difficult to read, either find a way to simplify it or refactor your program to eliminate it. In addition, parameters passed to the enumeration construction method are evaluated in a static context. Therefore, the lambda expression in the enumeration construction method cannot access the instance members of the enumeration. A constant-specific class body is still an effective method if the enumeration type has hard-to-understand constant-specific behavior that cannot be implemented within a few lines, or if an instance property or method needs to be accessed.
Again, you might think that anonymous classes are obsolete in the lambda age. This is closer to the truth, but there are some things you can do with anonymous classes that you can't do with Lambdas. Lambda is limited to functional interfaces only. If you want to create an instance of an abstract class, you can use an anonymous class, but you cannot use lambda. Similarly, you can use an anonymous class to create an interface instance with multiple abstract methods. Finally, Lambda cannot get a reference to itself. In lambda, the This keyword refers to an enclosing instance, which is usually what you want. In an anonymous class, the This keyword refers to an anonymous class instance. If you need to access the function object from within, you must use an anonymous class.
Lambdas and anonymous class sharing cannot reliably serialize and deserialize the implemented properties. Therefore, there should be little (if any) serialization of a lambda (or an anonymous class instance). If you have a function object that you want to serialize, such as a comparator, use an instance of a private static nested class (entry 24).
In summary, starting with Java 8, lambda is by far the best way to represent small function objects. do not use an anonymous class as a function object unless you must create an instance of a non-functional interface type . Also, keep in mind that lambda expressions make it so simple to represent small function objects that it opens a door to functional programming techniques that were not previously practical in Java.
Effective Java Third edition--42.LAMBDA expression is better than anonymous class