The JavaSE8, to be released in 2013, will contain a plan called Lambda Project, described in the JSR-335 draft of this year's June.
JSR-335 the closures into Java. Closures are present in many popular languages, such as C + + and C #. Closures allow us to create function pointers and pass them as parameters. In this article, we'll take a rough look at the Java8 feature and introduce the lambda expression. And I'm going to try to put some sample programs to explain some of the concepts and syntax.
The Java programming language provides us with the concept of an interface in which abstract methods can be defined. Interfaces define APIs and want users or vendors to implement these methods. Many times, we do not create separate implementation classes for some interfaces, and we write an inline interface implementation by writing an anonymous inner class.
Anonymous classes are very widely used. The most common scenario used by anonymous internal classes is the event handler. Second, anonymous inner classes are often used in multithreaded programs, and we usually write anonymous inner classes instead of creating implementation classes for runnable/callable interfaces.
As we discussed, an anonymous class is an implementation of an inline given interface. Usually we pass the object of this implementation class as a parameter to a method, and then the method will call the method of the implementation class passed over internally. This interface is called the callback interface, and these methods are called callback methods.
Although anonymous classes are used everywhere, they still have a lot of problems. The first major problem is complexity. These classes make the hierarchy of code look messy and complex, also known as Vertical Problem. Second, they cannot access non-final members of the encapsulated class. This keyword will become very confusing. If an anonymous class has a member name that is the same as its encapsulated class, an internal variable will overwrite the external member variable, in which case the external member will be invisible inside the anonymous class and may not even be accessible through the This keyword. Because the This keyword is worth the object of the anonymous class object itself, not his encapsulated class.
public void Anonymousexample () {
String nonfinalvariable = "Non Final Example";
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 Variable
->runnable Class Member
This example is a good illustration of the problem I mentioned above, and the LAMBDA expression solves almost all the problems that the anonymous inner class brings. Before we go further into lambda expressions, let's look at functional interfaces.
Functional Interfaces
Functional interfaces is an interface with only a single method, which represents the method contract.
Only one of the above definitions is actually not that simple. This paragraph is not understood, please read the original (the ' single ' method can exist in the form of multiple abstract methods that are inherited from Superinterfa Ces. But in, the inherited methods should logically represent a single or it might redundantly declare a That's provided by classes like Object, e.g. toString.)
The following example shows clearly how to understand the concept of functional interfaces.
Interface Runnable {void run ();}
Functional
Interface Foo {boolean equals (Object obj);}
Not functional; Equals is already a implicit member
interface Bar extends Foo {int compare (string O1, String O2);
functional; Bar has one abstract Non-object method
interface Comparator {
boolean equals (Object obj);
int compare (t O1, T O2);
}
functional; Comparator has one abstract Non-object method
interface Foo {int m (); Object Clone (); }
//Not functional-method Object.clone isn't public
interface X {int m (iterable arg);
interface Y {int m (iterable arg);}
Interface Z extends X, Y {}
//Functional:two methods, but they have the same signature
Most callback interfaces are functional interfaces. such as Runnable,callable,comparator and so on. Formerly known as SAM (Single Abstract method)
LAMBDA Expression
As we said above, one of the main problems with anonymous classes is that the hierarchy of the code looks messy, that is, Vertical Problem, lamdba expressions are actually anonymous, but their structure is lighter and shorter. Lambda expressions look like methods. They have a formal argument list and the block expression of these parameters.
(String s)-> S.lengh;
()->;
(int x, int y)-> x + y;
The above example means that the first expression receives a string variable as an argument and then returns the length of the string. The second takes no arguments and returns 43. Finally, the third accepts two integers x and y, and returns its and.
After reading a lot of text, finally, I can give an example of the first LAMBDA expression, this example runs under the JavaSE8 preview version:
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 (()-> {
//below 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 some of the differences between using LAMBDA expressions and using anonymous inner classes. We can clearly say that using LAMBDA expressions to write anonymous classes solves the problem of variable visibility. You can look at the comments in the code and the LAMBDA expression does not allow you to create override variables.
The syntax of the usual LAMBDA expression includes a list of arguments, and the arrow keyword "->" is the body at last. The body can be an expression (a single-line statement) or a multiline statement block. If it is an expression, it is evaluated and returned, and if a multiple-line statement block looks similar to the method's statement block, you can use return to specify the returned value. Break and continue can only be used within the loop.
Why choose this particular grammatical form, which is commonly used in C # and Scala at the moment, and is a common expression of Lambda expressions. This syntax design basically solves the complexity of anonymous classes. But at the same time he is very flexible, for example, if the method body is a single expression, curly braces and return statements are not required. The result of an expression is his own return value. This flexibility can keep your code simple.
Lambda expressions are used as anonymous classes, so they can be used flexibly in other modules or in other lambda expressions (nested lambda expressions).
Lambda expression is enclosed within methods parameter block.
The Target interface type is the methods parameter type.
String user = DoSomething (()-> list.getproperty ("propname");
Lambda expression is enclosed within a thread constructor
//target interface The type is contructors paramter. i.e ble
new Thread (()-> {
System.out.println ("Running in different Thread");
}). Start ();
If you look at the lambda expression carefully, you will see that the target interface type is not part of an expression. The compiler helps infer the type of the lambda expression and the surrounding environment.
Lambda expressions must have a target type, and they can fit any possible target type. When the target type is an interface, the following conditions must be met in order to compile correctly:
- Interface should be a functional interface
- The number and type of arguments for an expression must be consistent with the declaration in functional interface
- The return value type must be compatible with the return value type of the method in functional interface
- The thrown exception expression must be compatible with the throw exception declaration of the method in the functional interface
Because the compiler can learn the parameter types and numbers through the declaration of the target type, you can omit the argument type declaration in a LAMBDA expression.
Comparator C = (S1, s2)-> s1.comparetoignorecase (S2);
Also, if the method declared in the target type receives only one argument (most of the time), then the parentheses of the parameter can also not be written, for example:
Actionlistenr Listenr = Event-> event.getwhen ();
An obvious question is, why doesn't a LAMBDA expression need a specified method name?
The answer is: Lambda expressions can only be used for functional interface, and functional interface only one method.
When we determine a functional interface to create a LAMBDA expression, the compiler can perceive the signature of the method in the functional interface and check whether the given expression matches.
This flexible syntax helps us avoid Vertical Problem that use anonymous classes and does not bring horizontal Problem (single line statements are very long).
The syntax of LAMBDA expressions is context-sensitive, but these are not the first occurrences. The diamond operators added by the Java SE 7 also has the concept of inferring types through context.
void Invoke (Runnable r) {R.run ()}
void Future invoke (callable R) {return C.compute ()}//above are two methods
, bot H takes parameter of type functional interface
Future s = Invoke (()-> "Done");//which invoke'll be called?
The answer to the above question is to invoke the method that receives the callable parameter. In this case, the compiler resolves through overloads of different parameter types. When there is more than one applicable overload method, the compiler also checks the compatibility of the lambda expression with the corresponding target type. Simply put, the Invoke method above expects a return, but only one invoke method has a return value.
Lambda expressions can be explicitly converted to the specified target type, as long as they are compatible with the corresponding type. Looking at the following program, I have implemented three kinds of callable, and have converted it to the callable type.
public class Firstsightwithlambdaexpressions {public
static void Main (string[] args) {
list list = Arrays.aslist (
(callable) ()-> "Callable 1",
(callable) ()-> "Callable 2",
(callable) ()-> "Callable 3");
Executorservice e = Executors.newfixedthreadpool (2);
List futures = null;
try {
futures = E.invokeall (list);
New Firstsightwithlambdaexpressions (). Dumplist (futures);
} catch (Interruptedexception | Executionexception E1) {
e1.printstacktrace ();
}
E.shutdown ();
}
public void Dumplist (List list) throws Interruptedexception,
executionexception {for
(Future future:list) { C19/>system.out.println (Future.get ());}}
As we discussed earlier, anonymous classes cannot access non final variables in the surrounding environment. But there is no such restriction in lambda expressions.
Currently, the functional interfaces for this definition applies only to interfaces. I tried to create a lambda expression for an abstract class that had only one abstract method, but there was a compilation error. According to jsr-335, future versions of lambda expressions may support functional Classes.
Method reference
A method reference is used as a reference to a method without calling it.
Lambda expressions allow us to define an anonymous method and use it as an instance of the functional interface. Method references are similar to LAMBDA expressions, and they all need a target type, but the difference is that the method reference does not provide a method implementation, and they refer to a method of an existing class or object.
System::getproperty
"abc":: Length
string::length
super::tostring
arraylist::new
The above statement shows the general syntax for references to methods and constructors. Here we see the introduction of a new operator "::" (double colon). I don't know the exact name for this operator, but the JSR refers to it as a separator, and the Wikipedia page refers to it as a scope parser operator. As our reference, within the scope of this tutorial, we will simply use it as a separator.
The target reference, or the receiver, is placed behind the provider and separator. This forms an expression that can refer to a method. In the final declaration of the above code, the method's name is "new". This expression refers to the construction method of the ArrayList class (the next section discusses the reference to the constructor method)
Before I get to know this, I want you to look at the power of the method references, and I created a simple sort program for the Employee array.
import java.util.Arrays; import java.util.List; import
Java.util.concurrent.ExecutionException;
Import Java.util.concurrent.Future; public class 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, Employee::mycompare);
System.out.println ("After Sort:");
Dumpemployee (employees); public static void Dumpemployee (employee[] employees) {for (Employee emp:Arrays.asList (employees)) {System.
Out.print (emp.name+ ",");
} System.out.println ();
Class Employee {String name;
Employee (String name) {this.name = name;
public static int Mycompare (employee 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 nothing special, and Employee is a very simple class with only one name attribute. The static method Mycompare receives two Employee objects and returns a comparison of their names.
In the main method I created an array of different employee, and passed it along with a method reference expression (Employee::mycompare) to the Arrays.sort method.
Wait a minute, if we look at Javadoc, you'll find that the second parameter of the Sort method is Comparator type, but we pass a static method reference for Employee. The important question is here, I have neither let the Employee implement the comparable interface nor write a separate Comparator class, but the output does not have any problems.
Let's have a look at why. The Arrays.sort method expects a Comparator instance, and this Comparator is a functional interface, which means he has only one method, that is, compare. Here we also maliciously transmit a LAMBDA expression that provides an implementation of the Compare method in this expression. But in our, our Employee class already has a comparison method of its own. But their names are different, the type, number, and return values are the same, and here we can create a method reference and pass it to sort as the second argument.
When there are multiple methods with the same name, the compiler chooses the best match based on the target type. To get a sense of it, take a look at an example:
public static int Mycompare (employee 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 to use as a sort.
employee[] Employees = {New Employee ("Nick"), New Employee ("Robin"), New Employee ("Josh"), New Employee ("
Andy"), new Employee ("Mark")};
Integer[] INTs = {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 reference declarations in both lines of code are the same (Employee::mycompare), the only difference is the array we passed in, we don't need to pass a vague token to be known as the method reference, the compiler will help us check the first argument, and smart to find the right way.
Don't be misled by static methods. Oh, we can also create references to instance methods. For static methods we use the class name:: The method name to write the method reference, or, if it is a reference to the instance method, the object:: Method name.
The example above is pretty good, but we don't have to write a separate method for the comparison of integers because the integer has already implemented comparable and provides the implementation method CompareTo. So let's just use the following line:
Arrays.sort (INTs, Integer::compareto);
Do you feel a little puzzled to see here? No? Well, let me confuse you.
Here, the integer is a class name (rather than an instance like new Integer ()), whereas the CompareTo method is a member method (non-static) of the integer class. If you look at my description above, you will know that the method reference for the member method:: Before should be an object , but why the statements here are actually legal.
The answer is: This type of statement allows for use in certain types. An integer is a data type, and for a data type, this statement is allowed.
If we turn the Employee's method Mycompare to Non-static, and then use: Employee::mycompare, a compile error will occur: No suitable methods Found.
To construct a method reference
A constructor reference is used to refer to a constructed method without instantiating the specified class.
The construction method reference is a new feature of Javase 8. We can construct a reference to a construction method and pass it as a parameter to the target type.
When we use the method reference, we refer to an existing method to use them. Similarly, when using the constructor method reference, we create a reference to an existing constructor method.
In the previous section we have seen the syntax class name of the constructor reference:: New, which looks like a method reference. A reference to this construction method can be assigned to an instance of the target functional interfaces. A class may have multiple construction methods, in which case the compiler checks the type of functional interfaces and finally finds the best match.
It was difficult for me to write the first constructor reference, although I understood his syntax, but I didn't know how to use it and what it was for. Finally, I spent a long time trying, finally "ah, found ...", look at the following procedures.
public class Constructorreference {public
static void Main (string[] ar) {
myinterface in = myclass::new;
System.out.println ("->" +in.getmemyobject ());
}
Interface myinterface{
MyClass getmemyobject ();
}
Class myclass{
MyClass () {}
}
The output is:
It's kind of magical, isn't it, this interface and this class have nothing to do with the return value of the method declared in the interface, except for the MyClass type.
This example arouses another question in my mind: how to instantiate a constructor reference with parameters? Look at the following program:
public class Constructorreference {public
static void Main (string[] ar) {
Emlpoyeeprovider Provider = Employee:: New;
Employee emp = Provider.getmeemployee ("John");
System.out.println ("->employee Name:" +emp.name);
System.out.println ("->employee Age:" +emp.age);
}
Interface emlpoyeeprovider{
Employee getmeemployee (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 we finish this article, let's take a look at one of the coolest features in JavaSE8--default Methods
Default method (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 a number of abstract methods of the Declaration, all NON-ABSTRACT implementation classes must provide all the implementation of these abstract methods, even if these methods are not used or inappropriate in some special implementation classes. In the upcoming Java version, allow us to define the default implementation of methods in the interface. Don't say much nonsense, look below:
public class Defaultmethods {public
static void Main (string[] ar) {
Normalinterface instance = new NORMALINTERFAC Eimpl ();
Instance.mynormalmethod ();
Instance.mydefaultmethod ();
}
Interface normalinterface{
void Mynormalmethod ();
void Mydefaultmethod () default{
System.out.println ("-> mydefaultmethod");
}
Class Normalinterfaceimpl implements normalinterface{
@Override public
void Mynormalmethod () {
System.out.println ("-> mynormalmethod");
}
The output is:
Two methods are declared in the above interface, but the implementation class of this interface implements only one, because Mydefaultmethod is marked with the default modifier, and a method block is provided as the default implementation. Common overload rules are still valid here. If the implementation class implements the method in the interface, it will call the method in the class, otherwise the default implementation will be invoked.
Interfaces that integrate parent interfaces can add, change, and remove default implementations in the parent interface.
Interface parentinterface{
void Initiallynormal ();
void Initiallydefault () default{
System.out.println ("-> mydefaultmethod");
}
Interface Childinterface extends parentinterface{
void Initiallynormal () default{
System.out.println ("Now Default-> Initiallynormal ");
}
void Initiallydefault (); Now a normal method
}
In this example, Parentinterface defines two methods, one is normal, the other is implemented by default, and the sub-interface is simply reversed, adding the default implementation to the first method and removing the default implementation for the second method.
Suppose a class inherits class C, implements interface I, and C has a method, and a method that provides the default method in I is overloaded compatible. In this case, the method in C takes precedence over the default method in I, and even when the method in C is abstract, it is still preferred.
public class Defaultmethods {public
static void Main (string[] ar) {
interfaxe impl = new Normalinterfaceimpl ();
impl.defaultmethod ();
}
Class parentclass{public
void Defaultmethod () {
System.out.println ("->parentclass");
}
Interface interfaxe{public
void Defaultmethod () default{
System.out.println ("->interfaxe");
}
class Normalinterfaceimpl extends ParentClass implements interfaxe{}
The output is:
The second example is that my class implements two different interfaces, but each of the two interfaces provides the same declaration of the method with the default implementation. In this case, the compiler will not know what's going on, and the implementation class must select one of the two implementations. This can be done by using super in the following ways.
public class Defaultmethods {public
static void Main (string[] ar) {
firstinterface impl = new Normalinterfaceimpl ();
Impl.defaultmethod ();
}
Interface firstinterface{public
void Defaultmethod () default{
System.out.println ("->firstinterface");
}
Interface secondinterface{public
void Defaultmethod () default{
System.out.println ("->secondinterface ");
}
}
Class Normalinterfaceimpl implements Firstinterface, secondinterface{public
void Defaultmethod () {
SecondInterface.super.defaultMethod ();
}
The output is:
Now, we've read the Java closures. In this article, we have access to functional interfaces and Java Closure, Understanding Java Lambda Expressions, method references, and constructing method references. And we've also written the Hello World example of LAMBDA expressions.
JavaSE8 is coming soon, and I will be happy to embrace these new features, and perhaps some of these new features are still confusing, but I believe that as time goes on, it will be getting better.