Blog Address http://colobu.com
Java 8 released for a while, it was possible to focus on the lambda in Java 8 earlier and to be familiar with the most important language changes in Java 8. This article will delve into the lambda features in Java 8 and the stream interface to discuss some deep technical details.
For example, a lambda expression that serializes a reference to a captured context variable after deserialization. The lambda expression is recursive. The difference between a reference to a class method and a reference to an instance method. Problem with Diamond inheritance. The lazy and eager modes of the stream interface. The performance of the lambda.
Java Lambda syntax
Although you are already familiar with it, let's review the syntax of the lambda expression first.
"A lambda expression is a method:it provides a list of formal parameters and a Body-an expression or block-expressed In terms of those parameters, "
JSR 335
1 |
Function<integer, integer> fun = (integer x, integer y), {return x + y;} |
If the body has only one expression, you can omit the body's curly braces and return
.
1 |
Function<integer, integer> fun = (integer x, integer y), x + y |
Parameters can declare types, or they can be omitted based on type inference.
1 |
Function<integer, integer> fun = (x, y) x + y |
But not partially omitted.
A single argument can omit parentheses.
12 |
Function<integer, integer> fun = (x)-x+1function<integer, integer> fun = x-x+1 |
However, you cannot add a type declaration.
1 |
Function<integer, integer> fun = Integer x, x+1//wrong |
If there are no arguments, the parentheses are required.
12 |
1995 (), {System.GC ();} |
Java Lambda recursion
An anonymous function does not have a name, but a lambda expression can be assigned to a variable or passed as a parameter, which means it has a "name." So can you use this name for recursion?
Lambdafaq website said yes.
12 |
Function<long, long> fib = x, {if (x = =12return1Else return fib.apply (x-1) + x;}; System.out.println (Fib.apply (3L)); |
You can't actually compile this code because the compiler thinks the fib might not be initialized.
1 |
The local variable fib may not be been initialized |
Is there any way to pass the recursion?
There are some hacked methods, such as
12 |
null }; foo[0return 0)? 1: x* foo[0].applyasdouble (x1);}; |
or (the creation of a generic array is a little cumbersome)
1234567891011121314 |
@SuppressWarnings("Unchecked")Private Static<E> e[]NewArray(Class Clazz,intSize) {return(e[]) Array.newinstance (clazz, size); } Public Static void Main(string[] args)throwsInstantiationexception, Illegalaccessexception, SecurityException, nosuchmethodexception {//function<long, long> fib = x, {if (x ==1 | | x = = 2) return 1L; else return fib.apply (x-1) + x;};Function<long, long>[] funs = NewArray (Function.class,1); funs[0] = x, {if(x = =1|| x = =2)return 1LElse returnfuns[0].apply (X-1) + x;}; System.out.println (funs[0].apply (TenL));} |
Or use a helper class.
12 |
Bifunction<bifunction, Long, long> facthelper = (f, x), {if (x = =12return 1 Else return x + (long) f.apply (f,x-1);}; Function<long, long> fib = x, facthelper.apply (Facthelper, x); |
Capturing variables
Just like local classes and anonymous classes, lambda expressions can capture variables (capture variable).
In addition, a local class had access to local variables. However, a local class can only access local variables is declared final. When a local class accesses a local variable or parameter of the enclosing block, it captures that variable or parameter
But a lambda expression does not force you to declare a variable as final, as long as it behaves as if it were the final variable, which is equivalent to final.
The following example does s
not have to be declared final, and the actual plus final does not compile an error.
123 |
"Smallnest"; Runnable r = ()-System.out.println ("Hello" + s); R.run (); |
But the following example is s
not final, and the compilation will be wrong. ,
1234 |
"Smallnest"; Runnable r = ()-System.out.println ("Hello""Colobu"; R.run (); |
The following code will also compile unsuccessfully:
123 |
"Smallnest" "abc"; System.out.println ("Hello" + s);}; R.run (); |
Note that final is simply a variable that cannot be assigned, and the value of the variable field can be changed.
12345 |
New Sample (); S.setstr ("smallnest"); Runnable r = ()-System.out.println ("Hello" + s.getstr ()) S.setstr ("Colobu"); R.run (); |
Here we can change the value of the Str field of S.
Serialization of general serialization/deserialization
Lambda expressions can be serialized. The following is a simple example.
12345678910 |
Runnable r = (Runnable & Serializable) (), System.out.println ("Hello serialization"new FileOutputStream ("runnable.lambda"newnew FileInputStream ("Runnable.lambda" New ObjectInputStream (FIS); r = (Runnable) is.readobject (); R.run (); |
Note (Runnable & Serializable)
is the new syntax in Java 8. Cast an object to a intersection of types by adding multiple bounds.
Whether a lambda can be serialized, the parameters it captures, and whether the target type can be serialized. Of course, the use of serialization in practice is discouraged. The above example R implements the serializable interface, and there is no captured argument, so it can be serialized.
Serialization/serialization with capture variables
Let's look at an example with captured argument.
12345678910111213141516171819202122232425262728 |
Class Sample implements Serializable {PrivateString str; PublicStringGetstr() {returnSTR;} Public void Setstr(String str) { This. str = str;}} Public Static void Serializelambda()throwsException {Sample s =NewSample (); S.setstr ("Smallnest"); Sampleserializableinterface r = ()-System.out.println ("Hello"+ S.GETSTR ()); FileOutputStream fos =NewFileOutputStream ("Runnable.lambda"); ObjectOutputStream OS =NewObjectOutputStream (FOS); Os.writeobject (R); S.setstr ("Colobu");} Public Static void Deserializelambda()throwsException {FileInputStream FIS =NewFileInputStream ("Runnable.lambda"); ObjectInputStream is =NewObjectInputStream (FIS); Sampleserializableinterface r = (sampleserializableinterface) is.readobject (); R.run ();} |
You can see that it is serialized with captured argument together s
. Even if it is deserialized, captured argument is not the same s
.
The resulting output hello smallnest
.
Method reference
A method reference is an interesting feature that can be directly referenced by a method like a pointer. New operator "::" method used to refer to a class or instance.
static method Reference
1234 |
Ten One L System.out.println (Bf.apply (A, b)); |
Instance method reference
12 |
consumer<string> C = system.out::p rintln;c.accept ("Hello Colobu"); |
The method signature referenced in both cases is the same as the method signature of target type, and the method name is not necessarily the same.
Arbitrary instance Reference
12 |
"Barbara" "James" "Mary" "John" "Patricia" "Robert" "Michael" "Linda" }; Arrays.sort (Stringarray, string::comparetoignorecase); |
This is a very interesting way to use. You can refer to an instance of any one type. The parameter list for the equivalent lambda expression is (string a, string b), and the method reference calls A.comparetoignorecase (b).
Constructor reference
Another special method reference is a reference to a constructor.
A reference to a constructor is similar to a reference to a static method, except that the method name is new
. A class has more than one constructor, and the most appropriate constructor is chosen based on the target type.
Multiple inheritance
Because Java 8 introduces the attributes of the default method, Java also wants to encounter multiple inheritance problems in other languages like C + +. Here is a list of two typical multi-inheritance cases.
Triangular inheritance
The triangle inherits as shown.
A
|\
| \
| B
| /
C
12345678910 |
Class A {publica () {System.out.println ("A ()");} Public A (int x) {System.out.println ("A (int x)");}} |
Diamond Inheritance
Diamond Inheritance as shown
A
/\
/ \
B C
\ /
D
1234567891011121314151617181920212223 |
Interface A { defaultvoid say () {System.out.println ("A says");}} Interface B extends a{ defaultvoid say () {System.out.println ("B says");}} Interface C extends a{ defaultvoid say () {System.out.println ("C says");}} Class D implements A, B, c{ } |
Error in direct compilation. The reason is duplicate default methods.
You need to D
overload say
methods in, customize, or use the parent class/interface method. Pay attention to the wording接口.super.default_method_name
12345 |
Class D implements A, B, c{publicvoidsay() {B.super. Say ();}} |
Fork Type Inheritance
Fork type Inheritance as shown
B C
\ /
D
12345678910111213141516 |
Interface B { defaultvoid say () {System.out.println ("b says");}} Class C {public voidsay() {System.out.println ("C says");}} Class D extends C implements b{ } |
The above code output C says
.
Principle:
Basically, you can judge the implementation rules for multiple inheritance according to the following three principles.
- The class takes precedence over the interface. If a subclass inherits a parent class and the interface has the same method implementation. Then the definition in the interface is ignored. As a third example.
- Methods in subtypes take precedence over methods in the province type. As a first example.
- If none of the above conditions are satisfied, as in the second example, you must show overwrite/implement its method, or declare it as abstract.
Stream interface.
lazy
Methods and
eager
Method
There are many ways to add a new stream interface.
lazy
Example:
Filter does not immediately iterate through the list, it simply adds some "recipes" to the stream. Currently its method implementation will not be executed. It will not execute until a method of type eager is encountered.
eager
Example:
Principle:
Look at the return value of the method. If the stream object is returned, then it is lazy, and if another type or void is returned, it is eager and will be executed immediately.
Functional interface can only have one method?
Functional Interface is also known as single Abstract Method (SAM) or role Interface.
Can you declare only one method in the interface?
The above example also shows that you can define multiple default method in functional interface. In fact java.util.function many functional interface define the default method.
So, functional interface can declare multiple methods, except the default method? Look at an example.
12345 |
Interface MyI {void apply (int i); String toString ();} |
Myi declares two methods of apply and ToString (). Can it be used as the target type of a lambda expression?
12 |
MyI m = x-System.out.println (x); M.apply (ten); |
No problem, the code can be compiled properly, the program is running properly.
But, wait, not functional interface can only declare an abstract method?
In fact, you see the second method is very special, it is the same as the method signature of object. It is a method of implicit implementation of an object, so it can be ignored.
Again, interface Foo { boolean equals(Object obj); }
it is not a functional interface because there is no declaration of a method.
"' Java
Interface Foo {
int m ();
Object Clone ();
}
Nor is it a functional interface, because Object.clone is not of the public type.
Performance of Lambda
Oracle's performance engineer Sergey Kuksenko has a good performance comparison document: JDK 8:LAMBDA Performance Study, a detailed and comprehensive comparison of performance differences between LAMBDA expressions and anonymous functions. Here is the video. On page 16, the worst (capture) is the same as inner class, Non-capture is 5 times times better than inner class.
The Lambda Development Group also has a PPT, which also describes the performance of lambda, including capture and non-capture scenarios. It looks like the worst case of lambda performance internal classes is better.
Java 8 Lambdas-they is fast, very fast also has an article (which may need to flip the wall), indicating that the lambda expression is also as fast.
Java 8 Lambda Performance Comparison
Java 8 lambdas Depth study