The Javase8 released in 2013 will include a plan called Lambda Project, which is described in the JSR-335 draft in June this year.
JSR-335 introduced the closure into Java. The closure exists in many popular languages, such as C ++, C#. The closure allows us to create a function pointer and pass them as a parameter. In this article, we will roughly look at the characteristics of Java8 and introduce Lambda expressions. And I will try to put some sample programs to explain some concepts and grammar.
The Java programming language provides us with the concept of the interface, and the abstract method can be defined in the interface. The interface defines the API and hopes that users or suppliers to implement these methods. Many times, we do not create independent implementation classes for some interfaces. We write an anonymous internal class to write an interface implementation of the inner couplet.
Anonymous use is widely used. The most common scene used in anonymous internal class is event processor. Secondly, anonymous internal classes are often used in multi -threaded programs. We usually write anonymous internal classes instead of creating the implementation class of the Runnable/Callable interface.
Just like we are discussing, an anonymous class is the implementation of a given interface of a Ninja. Usually we pass the object of this implementation class as a parameter to a method, and then this method will call the implementation class method to the implementation class internally. Therefore, this interface is called a callback interface. These methods are called callback methods.
Although anonymous categories are used everywhere, they still have many problems. The first main problem is complexity. These classes make the levels of the code look messy and complicated, also known as Vertical Privem. Second, they cannot access the non -final members of the packaging class. The keyword of this will become very confusing. If an anonymous class has the same member name as its packaging class, internal variables will cover the external member variables. In this case, the external members will be invisible inside the anonymous category. access. Because this keyword is worthy of anonymous object itself rather than his encapsulation object.
Public void anonymousexample () {string nonfinalvariable = "nonformal exmple"; string variable = "OUTER METHOD VARIABLE"; New Thread (new runnable () {string variable = "Runnable Class Member"; Public Void Run () {String Variable = "Run Method Variable"; // Below Line Gives Compilation error. //System.out.println ("->" "" + NONFINALVARIable); System.out.println ("->" " + Variable); System. out.println ( "->" + this.variable);}}). Start ();} The output is:
-> Run Method Varial-> Runnable Class Member
This example explains the problem I mentioned above, and Lambda expression almost solves all the problems caused by anonymous internal classes. Before we further explore the Lambda expression, let's take a look at the Functional Interfaces.
Functional interfaces
Functional Interfaces is an interface with only a single method, which represents this method contract.
Only one in the definition above is not so simple. I do n’t understand this paragraph. Please readers to view the original text ETHODS Should Logically Repressent A Single Method or It Might Redantly Declare A Method that is provided by Classes Like Object, Eg Tostring.)
The following example clearly shows how to understand the concept of Functional Interfaces.
interface runnable {void run ();} // functionalInterface foo {Boolean equals (Object OBJ);} // not functional; equals is alream an implicit Interface Bar Extends Foo {int Compare (String O1, String O2);} // Functional; Bar HAS One Abstract non-Object Methodinter FORATOR {Boolean Equals (Object Obj); int compare (T O1, T O2); ONE Abstract non-object methodFace Foo {int m (); Object clone ();} // not functional; method object.clone is not publicinterface x {int M (ITerable ARG);} Internet y {int M (ITerable ARG);} Internet Z extends x, y {} // functional: Two Methods, but they have the same signatureMost of the callback interfaces are Functional Interfaces. For example, Runnable, Callable, Comparator, etc. It was previously called SAM (Single Abstract Method)
Lambda expression
As we said, a main problem of anonymous category is that the level of the code looks messy, that is, the Vertical Privem. Lamdba expression is actually anonymous, but their structure is lighter and shorter. Lambda expression looks like a method. They have a formal parameter list and blocking of these parameters.
(String s) -> s.Lengh; () -> 43; (int x, int y) -> x + y;
The above example means that the first expression receives a String variable as a parameter, and then returns the length of the string. The second one without any parameters and return 43. Finally, the third accepted two integer x and y, and returned to peace.
After reading a lot of words, finally, I can give an example of the first Lambda expression. This example runs under the preview of Javase8:
Public Class FirstlambdaexPression {Public String Variable = "Class Level Variable"; Public Static Void Main (String [] ARG) {New FIRSTLAMBDAEXPRESSION () .lambdaexpression ();} Public void lambdaexpression () {string variable = "Method Local Variable"; String nonFinalVariable = "this is non final variable"; new thread (() -> {// belW Line Gives compilation error // string variable = "run method variable" system.out. Println ("->" " + Variable); System.out.println ("->" "" " + This.variable);}). Start ();}} The output is:
-> Method Local Variable-> Class Level Variable
You can compare the difference between using Lambda expressions and anonymous internal class. We can clearly say that writing anonymous categories using Lambda expression solves the problem of variable visibility. You can take a look at the annotations in the code. Lambda expression does not allow creation of coverage variables.
The general Lambda expression syntax includes a parameter list, the arrow keyword "->" is the main body. The subject can be an expression (one -line statement) or a multi -line sentence. If it is an expression, it will be calculated and returned. If it is a multi -line sentence block, it seems that it is very similar to the methodical block of the method. You can use Return to specify the return value. Break and Continue can only be used inside the cycle.
Why choose this special grammar form, because currently this style is usually in C# and Scala, which is also a general writing of Lambda expression. This grammar design basically solves the complexity of anonymous type. But at the same time, he is also very flexible. For example, if the method is a single expression, the brackets and Return statements are unnecessary. The result of the expression is as his own return value. This flexibility can keep the code simple.
Lambda expression is used as anonymous, so they can be flexibly used in other modules or other Lambda expressions (nested LAMBDA expressions).
// Lambda Exposition is Enclosed with Methods Parameter Block.//Target Interface Type is the Methods Parameter Type. TPROPERTY ("PROPNAME"); // Lambda Expression is Enclosed with a thread constructor // Target interface type is Contructors Paramter IE Runnablenew Thread (() -> {System.out.println ("Running in Different Thread");}); Start ();
If you take a closer look at Lambda expression, you will see that the target interface type is not part of an expression. The compiler helps to infer the type and surrounding environment of Lambda expression.
Lambda expression must have a target type, and they can adapt to any possible target type. When the target type is an interface, the following conditions must be met to compile correctly:
Because the compiler can know the parameter type and number through the target type statement, in the Lambda expression, the parameter type declaration can be omitted.
Comparator C = (s1, s2) -> s1.comparetoignorecase (s2);
Moreover, if the method declared in the target type only receives only one parameter (many times this is the case), then the small brackets of the parameter can also be written, for example:
ActionListenr Listenr = Event-> Event.getwhen ();
A very obvious question comes, why does Lambda express not a specified method name?
The answer is: Lambda expression can only be used for the functional interface, while the functional interface has only one method.
When we determine a Functional Interface to create Lambda expressions, the compiler can perceive the signature of the Functional Interface Chinese law and check whether the given expression matches.
This flexible grammar helps us avoid using anonymous Vertical Privem, and it will not bring Horizontal Privem (very long one -way sentence).
Lambda expression grammar is related to context, but these are not the first time. Diamond Operators added by Java SE 7 also has this concept, which is inferred by context.
void invoke (runnable r) {R.Run ()} void Future Invoke (Callable R) {Return C.Compute ()} // ABOVE Are Two Methods ONAL InterfaceFuture s = Invoke (() -> "Done"); // Which Invoke Will Be Called?
The answer to the above question is to call the method of receiving the Callable parameter. In this case, the compiler will be resolved through the load of different parameter types. When there is more than one applicable loading method, the compiler also checks the compatibility of Lambda expression and the corresponding target type. Simply put, the INVOKE method above is expected to return, but only one Invoke method has a return value.
Lambda expressions can be explicitly converted into specified target types, as long as they are compatible with corresponding types. Looking at the following program, I implemented three types of Callable, and they all converted it into a class type.
Public Class FirstWithLambDaexpressions {Public Static Void Main (String [] ARGS) {listl = Arrayslist (CalLial)-> "CalLABLE 1", (Call ABLE) ()-> "Callable 2", (Callable) (Callable) )-> "CalLABLE 3"); ExecutorService E = Executors.newfixedThreadPool (2); list futures = null; TRY {Futures = E.invoKeall (List); New Fitsightwi Thlambdaexpressions (). Dumplist (futures);} Catch (interruptedException | ExecutionException E1) {e1.printstacktrace ();} e.shutdown ();} Public void dumplist (List list) Throws interuPtexception, ExecutionExcepti ON {For (Future Future: List) {System.out.println (Future.get ()) ;}}}As we discussed earlier, anonymous categories cannot access the non -final variables in the surrounding environment. But there is no such limit in Lambda expression.
At present, the defined Functional Interfaces is only applicable to the interface. I tried to create a lambda expression of only one abstract method, but a compilation error was made. According to JSR -335, the future version of Lambda expression may support Functional Classes.
Method quote
Methods referenced as a reference method without calling it.
Lambda expression allows us to define an anonymous method and use it as an instance of the Functional Interface. Methods are very similar to the Lambda expression. They all need a target type, but the difference is that the method quotes the implementation of the method that does not provide methods. They quote a method of existing class or object.
System :: getproperty "abc" :: playstring :: specificsuper :: tostringarrayList :: New
The above statement shows the general syntax of the method and the reference to the constructor. Here we have seen a new operating character ":::: Double colon). I don't know the exact name as this operator, but JSR refers to it as a separators, and the Wikipedia page refers to it as a scope analysis operation. As a reference, within the range of this tutorial, we will simply use it as a separatist.
The target reference or the receiver is placed behind the provider and separators. This forms an expression that can quote a method. In the final statement, the name of the method is "New". This expression quotes the structure method of the ArrayList class (the reference to the constructor in the next section)
Before you understand this, I want to let you see the strength of the method quoted. I created a simple Employee array of sorting procedures.
Import java.util.arrays; Import Java.util.List; Import Java.util.Concurrent.execuTionException; Import java.util.CUTIRRENT.FUTURE; SS METHODREFERENCE {Public Static Void Main (String [] AR) {Employee [] Employees = {New Employee ("Nick"), New Employee ("Robin"), New Employee ("Josh"), New Employee ("Andy"), New Employee ("Mark")}; System.out .println ( "BeFore Sort:"); Dumpemployee (Employees); Arrays.sort (Employees, Employeee :: mycompare); System.out.println ("After Sort:"); mployees); ] Employees) {for (Employee Emp: Arrays.aslist (Employees)) {System.out.print (Emp.name+",");} System.out.println (); {String name; EMPLOYEE ( String name) {this.Name = name;} Public Static int Mycompare (Employeee Emp1, Employee Emp2) {Return Emp1.name.Compareto (EMP2.Name);}} The output is:
Before Sort: Nick, Robin, Josh, Andy, Mark, After Sort: Andy, Josh, Mark, Nick, Robin,
The output is not special. Employee is a very simple class with only one name attribute. Static method Mycompare receives two Employee objects and returns their names to compare.
In the main method, I created a different array of an Employee, and passed it to the arrays.sort method to reference it to the array of expression (Employee :: mycompare).
Wait a minute, if we look at Javadoc, you will find that the second parameter of the Sort method is CORARATOR type, but we pass the static method reference of Employee. The important problem is here. I neither let EMPLOYEE implement the Comparable interface, nor did I write an independent Comparator class, but there is no problem with the output.
Let's take a look at why. Arrays.sort method expects an instance of a comparator, and this comparator is a functional interface, which means that he has only one method, that is, compare. Here we also maliciously pass on a Lambda expression, which provides the implementation of the Compaare method in this expression. But in us, our EMPLOYEE class already has a comparison method. It's just that their names are different. The type, quantity, and return values of the parameters are the same. Here we can create a method reference and pass it to SORT as the second parameter.
When there are multiple methods of the same name, the compiler will choose the best matching according to the target type. To understand, look at an example:
Public Static Int Mycompare (Employeee Emp1, Employee EMP2) {Return Emp1.Name.Compareto (Emp2.Name);} // Another Method with The Same Name as of the. Above.Public Static Int Mycompare (Integer Int1, Integer Int2) {{) {{) {{ Return int1.Compareto (int2);} I created two different arrays for sorting.
Employee [] Employees = {New Employee ("Nick"), New Employee ("Robin"), New Employee ("Josh"), New Employee ("Andy"), New Employee ("MARK")} ; Integer [] ins = {1, 4, 8, 2, 3, 8, 6}; Now, I execute the following two lines of code
Arrays.sort (Employees, Employee :: mycompare); arrays.sort (ints, employee :: mycompare);
Here, the method of references in the two lines of code is the same (Employee :: mycompare). The only difference is the array we introduced. The compiler helps us check the first parameter and find the right method in intelligence.
Don't be misled by the static method, we can also create a reference to the example method. For static methods, we use class names :: method name to write method reference. If it is a reference to the instance method, it is the object :: Method name.
The above example is quite good, but we don't have to write a method for the comparison of integer, because Integer has implemented Comparable and provides a implementation method Compareto. So we just use the following line directly:
Arrays.sort (ints, integer :: compareto);
Seeing this, do you feel a little confused? No? Then let me confuse you here. Integer is a class name (not an instance like New Integer ()), but the compareto method is a member method of the Integer class (non -static). The description will know that the method of the member method is referenced :: It should be an object before, but why the sentence here is indeed legitimate.
The answer is: this type of statement allows use in some specific types. Integer is a data type, and for the data type, this statement is allowed.
If we turn the Employee method mycompare into non -static, and then use: Employee :: mycompare, there will be compilation errors: no suitable method found.
Constructive method reference
The constructor references is used as a class that references a constructor without institutionalization specified.
The constructive method reference is a new feature of Javase 8. We can construct a reference to a constructive method and pass it as a parameter to the target type.
When we use it to reference, we quote one existing method to use them. Similarly, when using the constructive method reference, we create a reference to existing constructive methods.
In the previous section, we have seen the grammar name referenced by the constructor :: New, which looks like a method reference. The reference to this constructed method can be assigned to instances of the target Functional Interfaces. There may be multiple constructors in a class. In this case, the compiler will check the type of Functional Interfaces, and finally find the best match.
For me, it is difficult to write the first constructive method. Although I understand his grammar, I don't know how to use it and what it is useful. In the end, I spent a long time working hard, and finally "Ah, I found ...", look at the following procedure.
Public Class ConstructorReferences {Public Static Void Main (String [] AR) {MyInterface in = MyClass :: New; System.out.println ());}} interface MyInterface {MyClass GetMemyObject () } Class MyClass {MyClass () {}} The output is:
-> com.myclass@34e5307e
This looks a bit amazing, right? This interface and the return value of this class except the method declared in the interface are MyClass type, which has nothing to do.
This example aroused another problem in my heart: how to instantiate a constructive method with a parameter? Look at the procedure below:
Public Class ConstructorReferences {Public Static Void Main (String [] AR) {EMLPOYEPRODER PROVIDER = EMPLOYEE :: NEW; mployee ("John", 30); System.out.println ("-> Employee name:" +Emp.Name); System.out.println ("-> Employee Age:"+Emp.age);}} Interface Emlpoyeeprovider {Employeeeeeeeeeeee (String S, Integer I) ;} Class Employee {String name; Integer Age; Employee (string name, integer age) {this.Name = name; this.age = age;}}} The output is:
-> Employee Name: John-> EMPLOYEE AGE: 30
Before reading this article, let's take a look at the coolest feature in Javase8-default method
Default Methods
Javase8 will introduce a concept called the default method. The early Java version of the interface has a very strict interface. The interface contains some statement of abstract methods. All non -abstract implementation classes must provide all of these abstract methods. In the implementation class. In the upcoming Java version, the default implementation of the method in the interface is allowed. Not much nonsense, look at the following:
Public Class DefaultMethods {Public Static Void Main (String [] AR) {NormalInterface Instance = New NormalInterfaceimpl (); Instance.MynormalMethod () ; Instance.myDefaultMethod ();}} interface normalInterface {void mynormalmetHod (); void mydefaultMethThod () {System.out.println ("-> MyDefaultMethod");}} Class NormalInterfaceIMPL IMPLEMELEMERINTERFACE {@Override Public Void MyNormalMethod () {) System.out.println ("-> MynormalMethod");}} The output is:
-> MyDefaultMethod
The above interface declares two methods, but the implementation class of this interface only realizes one of them, because MyDefaultMethod uses the Default modifiers, and it provides a method block for the default implementation. GM's heavy load rules still take effect here. If the implementation class implements the method in the interface, it will be the method in the call class when calling, otherwise, the default implementation will be called.
The interface of the integrated parent interface can increase, change, and remove the default implementation of the parent interface.
Interface Parentinterface {void initiallynomal (); void InitiallyDefault () default {System.out.println ("->> /> MyDefaultMetmethod"); CE ChildInterface Extends Parentinterface {Void Initiallynomal () default {System.out.println ("Now Default- > initiallynomal ");} Void Initiallydefault (); // Now a NORMAL METHOD} In this example, Parentinterface defines two methods, one is normal, and the other is implemented by default. The sub -interface is simply reversed. The first method is added to the first method. Implementation by default.
Imagine a class inherited the class C, realized the interface I, and C had a method, and the method that provided the default method in which provided the default method was compatible. In this case, the method in C will give priority to the default method in i, and even the method in C is still priority when the method is abstract.
Public Class DefaultMethods {Public Static Void Main (String [] AR) {Interfaxe Impl = New NormalInterfaceimpl (); Impl.DefaultMethod (); SS Parentclass {Public Void DEFAULTMETHOD () {System.out.println ("-> Parentclass ");}} interface interfaxe {public void defaultMethod () default {system.out.println ("-> interfaxe "); Arentclass Implements interfaxe {} The output is:
-> Parentclass
The second example is that my class has implemented two different interfaces, but both of the two interfaces provide the same statement with the same default implementation method. In this case, the compiler will not be able to figure out what is going on. The implementation class must choose one of the two. This can be done in the following ways.
Public Class DefaultMethods {Public Static Void Main (String [] AR) {FirstInterface Impl = New NORMALINTERFAMPL (); Impr.DefaultMethod (); interface firstinterface {public void defaultMethod () default {system.out.println ("-> FirstInterface ");}} interface secondInterface {public void defaultMethod () default {system.out.println ("-> secondInterface "); LinterfaceIMPL Implements FirstInterface, SecondInterface {Public Void DEFAULTMETHOD () {SecondInterface.Super.DefaultMethod ( );}} The output is:
-> SecondInterface
Now, we have read the introduction of the Java closure. In this article, we came into contact with Functional Interfaces and Java Closure, which understood Java's Lambda expression, method references, and constructor references. And we also wrote a hello world example of Lambda expression.
Javase8 is coming soon. I will be happy to embrace these new features. Maybe these new features are still confused, but I believe that over time, it will become better and better.