Java8 Lambda expression Tutorial

Source: Internet
Author: User
Tags generator instance method

1. What is a lambda expression

The lambda expression is essentially an anonymous method. Let's take a look at the following example:

public int Add (int x, int y) {
return x + y;
}

This is what it looks like after turning into a lambda expression:

(int x, int y), x + y;

Parameter types can also be omitted, and the Java compiler is inferred from the context:

(x, y), x + y; Returns the sum of two numbers

Or

(x, y), {return x + y;}//indicates the return value explicitly

The lambda expression is visible in three parts: A parameter list, an arrow (-a), and an expression or a block of statements.

The lambda expression in the following example has no parameters and no return value (equivalent to a method that accepts 0 parameters and returns void, which is actually an implementation of the Run method in the runnable):

(), {System.out.println ("Hello lambda!");}

If there is only one parameter and can be inferred by Java, the parentheses of the argument list can also be omitted:

C--{return c.size ();}

The type of the 2.λ expression (is it object?). )

The lambda expression can be treated as an object (note the wording). The type of the lambda expression, called the target type. The target type of the lambda expression is the function interface (functional interface), which is the new concept introduced by JAVA8. Its definition is: an interface, if there is only one explicitly declared abstract method, then it is a function interface. Generally marked with @functionalinterface (also can not be marked). Examples are as follows:

@FunctionalInterface
public interface Runnable {void run ();

Public interface Callable<v> {V call () throws Exception;}

public interface ActionListener {void actionperformed (ActionEvent e);}

public interface Comparator<t> {int compare (t O1, T O2); Boolean equals (Object obj);}

Note the last comparator interface. It declares two methods, seemingly incompatible with the definition of a function interface, but it is indeed a function interface. This is because the Equals method is object, and all interfaces declare the public method of object-though mostly implicit. So, comparator explicitly declares that equals does not affect it is still a function interface.

You can assign a value to a function interface with a lambda expression:

Runnable R1 = () {System.out.println ("Hello lambda!");};

Then assign a value to an object:

Object obj = r1;

But you can't do this:

Object obj = () {System.out.println ("Hello lambda!");}; error! Object is not a functional interface!

You must explicitly transform into a function interface to:

Object o = (Runnable) (), {System.out.println ("Hi");}; Correct

A lambda expression can be used as an object only after it has been transformed into a function interface. So the following sentence cannot be compiled:

System.out.println ((), {}); Error! Unknown target type

Must first be transformed:

System.out.println ((Runnable) (), {}); That's right

Let's say you wrote a function interface that looks exactly like runnable:

@FunctionalInterface
Public interface Myrunnable {
public void run ();
}

So

Runnable R1 = () {System.out.println ("Hello lambda!");};
MyRunnable2 r2 = () {System.out.println ("Hello lambda!");};

Are the correct wording. This means that a lambda expression can have multiple target types (function interfaces), as long as the function matches successfully.
Note, however, that a lambda expression must have at least one target type.

The JDK pre-defines a number of function interfaces to avoid repeated user definitions. The most typical is function:

@FunctionalInterface
Public interface Function<t, r> {
R apply (t T);
}

This interface represents a function that takes a parameter of type T and returns a return value of type R.

Another predefined function interface is called consumer, and the only difference with function is that it has no return value.

@FunctionalInterface
Public interface Consumer<t> {
void Accept (T t);
}

There is also a predicate, used to determine whether a condition is satisfied. It is often used for sieve filtering operations:

@FunctionalInterface
Public interface Predicate<t> {
Boolean Test (T T);
}

In summary, a lambda expression is actually defined as an anonymous method, except that this method must conform to at least one function interface.

use of 3.λ expressions

Where 3.1λ expressions are used

Lambda expressions are primarily used to replace previously widely used internal anonymous classes, various callbacks such as event responders, runnable of incoming thread classes, and so on. Look at the following example:

Thread oldschool = new Thread (new Runnable () {
@Override
public void Run () {
System.out.println ("This was from an anonymous class.");
}
} );

Thread GAODUANDAQISHANGDANGCI = new Thread ((), {
System.out.println ("This was from a anonymous method (lambda exp).");
} );

Note that the lambda expression of the second line thread, you do not need to explicitly turn it into a runnable, because Java can be automatically inferred from the context: a thread constructor takes a runnable parameter, and the lambda expression passed in exactly matches its run () function. So the Java compiler infers that it is runnable.

In form, the lambda expression simply saves you a few lines of code. But the motivation to introduce lambda expressions into Java is not just for that. JAVA8 has a short-term goal and a long-term goal. The short-term goal is to tie in with the internal iterations and parallel processing of the collection class batch operation (as described below); the long-term goal is to steer Java into a functional programming language (not to completely become a functional programming language, but to have more functional programming language features), and for this reason, Instead of simply using internal classes to implement lambda expressions, Oracle uses a strategy that is more dynamic, flexible, and easy to scale and change in the Future (invokedynamic).

3.2λ expression and Collection class batch operation (or call block operation)

The batch operation for the collection class is mentioned above. This is another important feature of JAVA8, and its use in conjunction with lambda expressions is the main feature of Java8. The purpose of the batch operations API for the collection class is to implement the "internal iteration" of the collection class and expect to take advantage of modern multicore CPUs for parallel computing.
The iteration (iteration) of the collection class before Java8 is external, that is, the customer code. An internal iteration means that the Java class Library is iterated instead of the customer code. For example:

for (Object o:list) {//external iteration
System.out.println (o);
}

Can be written as:

List.foreach (o, {System.out.println (o);}); The Foreach function implements an internal iteration

The collection class (including list) now has a foreach method that iterates over the elements (traversal), so we don't need to write the For loop anymore. The Foreach method accepts a function interface consumer to do arguments, so you can use the lambda expression.

This internal iterative approach exists in a wide variety of languages, such as the STL algorithm Library of C + +, Python, Ruby, Scala, and so on.

Java8 introduces another important concept for the collection class: stream. A flow often takes a collection class instance as its data source, and then defines the various actions on it. The Flow API design uses the pipeline (pipelines) mode. A single operation of the convection returns another stream. Like the IO API or the StringBuffer append method, many different operations can be strung together in a single statement. Look at the following example:

list<shape> shapes = ...
Shapes.stream ()
. filter (S-s.getcolor () = = BLUE)
. ForEach (S-s.setcolor (RED));

First call the stream method to create a stream with the elements inside the shapes of the collection class object as the data source. Then call the filter method on this stream, pick out the blue one, and return to the other stream. Finally, the Foreach method is called to spray these blue objects into red. (The Foreach method no longer returns a stream, but a terminal method, similar to the StringBuffer after several append are called)

The parameter of the filter method is the predicate type, and the parameters of the Foreach method are the consumer types, which are function interfaces, so lambda expressions can be used.

There is also a method called Parallelstream (), as the name implies it and stream (), just indicate to parallel processing, in order to make full use of the modern CPU multicore features.

Shapes.parallelstream (); or Shapes.stream (). Parallel ()

Take a look at more examples. The following is a typical big data processing method, Filter-map-reduce:

Give an array of type string to find all the non-repeating primes
public void Distinctprimary (String ... numbers) {
list<string> L = arrays.aslist (numbers);
list<integer> r = L.stream ()
. Map (e-New Integer (e))
. Filter (E-primes.isprime (e))
. DISTINCT ()
. Collect (Collectors.tolist ());
System.out.println ("distinctprimary result is:" + R);
}

The first step is to pass in a series of strings (which are assumed to be valid numbers), go to a list, and then call the stream () method to generate the stream.

Step Two: The map method of the call flow turns each element from string to integer and gets a new stream. The map method takes a parameter of a function type, which is described above, and functions are functional interfaces, so here is a lambda expression.

Step Three: Call the filter method of the stream, filter those numbers that are not primes, and get a new stream. The filter method accepts a parameter of type predicate, described above, predicate is a function interface, so here is the lambda expression.

Fourth step: Call the distinct method of the stream, remove the duplicates, and get a new stream. This is essentially another filter operation.

Fifth step: Use the Collect method to collect the final result into a list. The Collect method accepts a parameter of type collector, which indicates how the final result is collected. In this example, the results are simply collected in a list. We can also use Collectors.tomap (E->e, e->e) to collect the results into a map, which means: the results receive a map, using the primes themselves as both a key and as a value. The Tomap method accepts parameters of two function types, which are used to generate the keys and values, and function is an interface, so lambda expressions are used here.

You might think that in this example, the List L has been iterated several times, map,filter,distinct are a cycle, and the efficiency will be bad. This is not actually the case. These methods that return another stream are lazy (lazy), and the Collect method that returns the final result is "urgent" (eager). The lazy method does not execute until the eager method is encountered.

When the eager method is encountered, the preceding lazy method is executed sequentially. And it is pipeline through type execution. This means that each element passes through these pipelines sequentially. For example, there is an element "3", first it is map into an integer type 3, and then through the filter, found is the prime number, is retained, and through the distinct, if there is already a 3, then directly discarded, if not yet retained. In this way, 3 operations actually go through only one cycle.

In addition to collect outside the eager operation also has foreach,toarray,reduce and so on.

Here's a look at perhaps the most commonly used collector method, Groupingby:

Give an array of type string, find each of the primes and count the number of occurrences
public void Primaryoccurrence (String ... numbers) {
list<string> L = arrays.aslist (numbers);
Map<integer, integer> r = L.stream ()
. Map (e-New Integer (e))
. Filter (E-primes.isprime (e))
. Collect (Collectors.groupingby (p->p, Collectors.summingint (p->1)));
System.out.println ("primaryoccurrence result is:" + R);
}

Note This line:

Collectors.groupingby (P->p, Collectors.summingint (p->1))

It means: The result is collected into a map, with the number of the count of each prime itself as a key, its occurrence as a value.

Here is an example of reduce:

Gives an array of type string, with all the non-repeating primes and
public void Distinctprimarysum (String ... numbers) {
list<string> L = arrays.aslist (numbers);
int sum = L.stream ()
. Map (e-New Integer (e))
. Filter (E-primes.isprime (e))
. DISTINCT ()
. Reduce (0, (x, y)-x+y); Equivalent to. SUM ()
System.out.println ("Distinctprimarysum result is:" + sum);
}

The reduce method is used to produce a single final result.
The stream has many predefined reduce operations, such as SUM (), Max (), Min (), and so on.

And a real-world chestnut, for example:

Number and proportion of men and women aged 25-35
public void Boysandgirls (list<person> persons) {
Map<integer, integer> result = Persons.parallelstream (). Filter (P, p.getage () >=25 && p.getage () <=35).
Collect
Collectors.groupingby (P->p.getsex (), Collectors.summingint (p->1))
);
System.out.print ("Boysandgirls result is" + result);
System.out.println (", ratio (Male:female) is" + (float) result.get (person.male)/result.get (Person.female));
}


More usage of 3.3λ expressions

nested λ-expressions
callable<runnable> C1 = (), {System.out.println ("Nested Lambda");
C1.call (). run ();

Used in conditional expressions
callable<integer> C2 = true? ((): (()-24);
System.out.println (C2.call ());

Define a recursive function that must be qualified with this
protected unaryoperator<integer> factorial = i-i = = 0? 1:I * this.factorial.apply (I-1);
...
System.out.println (Factorial.apply (3));

In Java, it is not possible to declare Tiaogan in the same way, for example, by declaring a lambda expression (x, y), X + y, and attempting to invoke it by passing in an actual parameter (2, 3):

int five = ((x, y) x + y) (2, 3); error! Try to call a lambda in-place

This is possible in C + +, but not in Java. The lambda expression for Java can only be used as an assignment, a parameter, a return value, and so on.

4. Other Related Concepts

4.1 Captures (capture)

The concept of capture is to solve the problem of which external variables we can use in the lambda expression (that is, in addition to its own parameters and internally defined local variables).

The answer is: very similar to inner classes, but with different points. The difference is that the inner class always holds a reference to its outer class object. The lambda expression, however, does not hold a reference to the object unless it uses the method or member of its outer class (enclosing Class) object inside it.

Before Java8, if you want to access a local variable of an external object in an inner class, the variable must be declared final. In Java8, this restriction was removed and replaced by a new concept, "effectively final". It means that you can declare it as final, or you can not declare final but use it as final, that is, the assignment never changes. In other words, make sure it does not compile with the final prefix.

In Java8, both the inner class and the lambda expression can access the effectively final local variables. Examples of lambda expressions are as follows:

...
int TMP1 = 1; Member variables that surround a class
static int tmp2 = 2; Static member variables that surround a class
public void Testcapture () {
int Tmp3 = 3; Not declared as final, but effectively final local variables
Final int tmp4 = 4; Local variables declared as final
int TMP5 = 5; Normal local variables

Function<integer, integer> f1 = i-i + tmp1;
Function<integer, integer> F2 = i-i + tmp2;
Function<integer, integer> f3 = i-i + Tmp3;
Function<integer, integer> f4 = i-i + tmp4;
Function<integer, integer> f5 = i, {
TMP5 + = i; Compile wrong! Assigning a value to TMP5 causes it to be not effectively final
return TMP5;
};
...
TMP5 = 9; Compile wrong! Assigning a value to TMP5 causes it to be not effectively final
}
...

The reason Java requires a local variable final or effectively final is a multithreaded concurrency problem. Internal classes, lambda expressions can be executed in different threads, allowing multiple threads to simultaneously modify a local variable that does not conform to the Java design concept.

4.2 Methods Reference (method Reference)

Any lambda expression can represent an anonymous descriptor for the unique method of a function interface. We can also use a specific method of a class to represent this descriptor, called a method reference. For example:

Integer::p arseint//static method reference
System.out::p rint//instance method reference
Person::new//Constructor reference

Here is a set of examples that teach you to use a method reference instead of a lambda expression:

   //c1 is the same as C2 (static method Reference)
    comparator<integer> c2 = (x, y) integer.co Mpare (x, y);
    comparator<integer> c1 = integer::compare;
   
   //The following two sentences are the same (instance method reference 1)
    Persons.foreach (E- System.out.println (e));
    Persons.foreach (system.out::p rintln);
   
   //The following two sentences are the same (instance method reference 2)
    Persons.foreach (Person- > Person.eat ());
    Persons.foreach (person::eat);
   
   //The following two sentences are the same (constructor reference)
    Strlist.stream (). Map (s) New Integer (s));
    Strlist.stream (). Map (integer::new);
   
Use the method reference, your program will become shorter. Now the Distinctprimarysum method can be rewritten as follows:

public void Distinctprimarysum (String ... numbers) {
list<string> L = arrays.aslist (numbers);
int sum = L.stream (). Map (Integer::new). Filter (Primes::isprime). Distinct (). sum ();
System.out.println ("Distinctprimarysum result is:" + sum);
}

There are other ways to quote:

Super::tostring//Referencing the parent class method of an object
String[]::new//constructor that references an array

4.3 default method

In Java8, an interface declaration can have a method implemented, called the default method. Prior to this, the methods in the interface were all abstract methods.

Public interface Myinterf {

String M1 ();

Default String m2 () {
Return "Hello default method!";
}

}

This actually confuses interfaces and abstract classes, but one class can still implement multiple interfaces, but only one abstract class.

The reason for this is this: because the collection library needs to add new methods to the batch operation, such as foreach (), stream (), etc., but cannot modify the existing collection interface--if you do that, all the implementation classes are modified, Includes many customer-made implementation classes. So we had to use this compromise approach.

In this way, we are confronted with a similar problem of multiple inheritance. If the class sub inherits two interfaces, Base1 and Base2, and the two interfaces have exactly the same two default methods, a conflict occurs. The sub class must then explicitly indicate by overloading the implementation of which interface to use (or provide its own implementation):

public class Sub implements Base1, Base2 {

public void Hello () {
Base1.super.hello (); Implementation of using BASE1
}

}

In addition to the default method, the Java8 interface can also have a static method implementation:

    public interface Myinterf {
   
         String M1 ();
       
        default String m2 () {
            return "Hello default method!";
       }
       
        static String m3 () {
            Return "Hello static method in interface!";
       }
       
   }
   
4.4 generator Functions (Generator function)

Sometimes a stream's data source is not necessarily an existing collection object, or it may be a "generator function." A generator function produces a series of elements that supply a stream. Stream.generate (supplier<t> s) is a generator function. Where the parameter supplier is a function interface, there is a unique abstract method <T> get ().

The following example generates and prints 5 random numbers:

Stream.generate (math::random). Limit (5). ForEach (System.out::p rintln);

Note that this limit (5), if there is no such call, then the statement will be executed forever. This means that the generator is infinite. This call is called an end operation, or a short-circuit (short-circuiting) operation.

References:
http://openjdk.java.net/projects/lambda/
Http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

Java8 Lambda expression Tutorial

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.