Java8 new Features (i)-Lambda

Source: Internet
Author: User
Tags stream api
This is a creation in Article, where the information may have evolved or changed.

Java8 new Features (i)-Lambda

In recent days have been using and research Golang, for a long time did not care about Java-related knowledge, a few days ago to see JAVA9 has been formally released, realize that their Java knowledge has lagged a lot, the mind inexplicable anxiety, decided to pull the knowledge to fill up.

The origin of Lambda expressions

As one of the most important updates in recent years, JAVA8 has brought many new features to developers, which may have been realized in many other languages, but it's better to be late than never. Lambda expressions are one of the most important features that JAVA8 brings.

Lambda expressions give Java8 support for some functional programming. Although the LAMBDA expression is not exactly the same as the closure, it also basically implements the function of the closure. Unlike other functional languages, LAMBDA expressions in Java are objects and must be attached to a particular type of object, a functional interface.

Why LAMBDA expressions are required

Internal Loop VS. Outer Loop

Let's look at a very simple example to print all the elements in the list:

        List<Interger> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)        for (int number: bumbers) {            System.out.println(number)        }

As a Java developer, you may have written countless similar codes in your life. It looks like it's fine, there's nothing to improve, we explicitly iterate through the elements within the list in an outer iteration, and process the elements in one step. So why promote internal iterations, because internal iterations help JIT optimization, and JIT can parallelize the process of processing elements.

The internal iterations need to be implemented with guava or other third-party libraries prior to JAVA8, and in Java8 we can do this with the following code:

        list.forEach(new Consumer<Integer>() {            @Override            public void accept(Integer integer) {                System.out.println(integer);            }        });

The above code is a little cumbersome, you need to create an anonymous class, using a lambda expression, you can greatly simplify the code

        list.forEach((a) -> System.out.println(a));

Java 8 also introduces a double-colon operator for class method references, which can be further simplified to

        list.forEach(System.out::println);

Inner Loop describes what you are going to do, more in line with the logic of natural language description

Passing Behavior,not only value

With lambda expressions, we can pass in the parameter, not only the value can be passed in, but also the related behavior can be passed in, so as to achieve a more abstract and common, more reusable API. Looking at the code example, you need to implement a method that asks for all the elements in the list, well, it looks simple.

public int sumAll(List<Integer> numbers) {    int total = 0;    for (int number : numbers) {        total += number;    }    return total;}

At this time, there is a need to implement a list of all the even and the method, simple, the code copied again, slightly modified.

public int sumAllEven(List<Integer> numbers) {    int total = 0;    for (int number : numbers) {        if (number % 2 == 0) {            total += number;        }    }    return total;}

Also did not send much effort, still need to improve, this time again need all odd and, different needs come over, you need to copy the code over and over again. Is there a more elegant solution? We are reminded of our lambda expression, Java 8 introduces a new function interface Predicate<T> , which is used to define the filter, the code is as follows

public int sumAll(List<Integer> numbers, Predicate<Integer> p) {    int total = 0;    for (int number : numbers) {        if (p.test(number)) {            total += number;        }    }    return total;}

So that the above two methods can be implemented by this method, and can be very easily extended, when you need to implement the element filter with other conditions to sum, only need to implement the filter condition of the lambda expression, as follows

        System.out.println(sumAll(list, (a)-> true));           \\ 所有元素和        System.out.println(sumAll(list, (a) -> a % 2 == 0));    \\ 所有偶数和        System.out.println(sumAll(list, (a) -> a % 2 != 0));    \\ 所有奇数和

Some students say that before we do not use lambda expressions, we can also implement them with interfaces. Yes, similar effects can be achieved with interface + anonymous classes, but lambda expressions are more intuitive, code is simple, readability is strong, and developers are more motivated to use similar code.

Helps write more elegant and readable code

Look at the code first:

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);                for (int number : list) {            if (number % 2 == 0) {                int n2 = number * 2;                if (n2 > 5) {                    System.out.println(n2);                    break;                }            }        }

This code is not difficult to understand, take the list of even, multiplied by 2 after the first number greater than 5, this code does not look difficult, but when you add more logic in the actual business code, it will appear poor readability. After refactoring this code using Java 8 's newly added stream API and lambda expression, the following

        System.out.println(                list.stream()                        .filter((a) -> a % 2 == 0)                        .map((b) -> b * 2)                        .filter(c -> c > 5)                        .findFirst()        );

A line of code to achieve the above functions, and readability, from doing to the right to read the past, first filter even, multiply by 2, then filter more than 5 number, take the first number. And the stream API is a lazy API, and does not occupy the extra space, such as the above code, does not have all the elements in the list to traverse, when the first meet the requirements of the element will be stopped.

LAMBDA expression Syntax

The syntax of a LAMBDA expression is defined in Java 8 specification 15.27, and some examples are given

() -> {}                    // 无参数,body 为空() -> 42                    // 无参数,表达式的值作为返回() -> {return 42;}          // 无参数,block 块() -> {System.gc();}() -> {    if (true) return 23;    else {        return 14    }}(int x) -> {return x + 1;}  // 有参数,且显式声明参数类型(int x) -> x + 1            (x) -> x + 1                // 有参数,未显式声明参数类型,编译器推断参数类型x -> x + 1          (int x, int y) -> x + y(x, y) -> x + y         (x, int y) -> x + y         // 非法, 参数类型显示指定不能混用

To summarize:

    • A LAMBDA expression can have 0, one, or more parameters.
    • The type of the parameter can be declared explicitly, or the parameter type can be inferred automatically from the context by the compiler.
    • Parameters are enclosed in parentheses, separated by commas. For example (A, b) or (int a, int b) or (String A, int b, float c)
    • An empty parenthesis is used to represent a set of empty parameters.
    • When there is only one argument, and the type is not explicitly indicated, the parentheses can be omitted
    • The body of a LAMBDA expression can contain 0, one, or more statements.
    • If the body of a LAMBDA expression has only one statement, the curly braces do not have to be written
    • If the body of a LAMBDA expression has more than one statement that must be included in the code block

Functional Interface (function interface)

There is also a question that is not mentioned in the above, how to represent a LAMBDA expression at the time of declaration? For example, a function can accept a lambda expression as input. Java 8 introduces a new concept called a function interface. In fact, it's not something new, the function interface is an interface that contains only an abstract method (which can contain other default methods), and Java 8 introduces a new annotation @FunctionalInterface , although it FunctionalInterface can be used without annotations, but using annotations to explicitly declare the interface as a function interface, And when the interface does not conform to the function interface requirements, an error is thrown during compilation. Many of the interfaces that were already in Java have been added to this note, most commonly such as Runnable

@FunctionalInterfacepublic interface Runnable {    public abstract void run();}

That is, you can now start a thread with a new LAMBDA expression

new Thread(    () -> System.out.println("hello world")).start()

Previously existing interfaces also have

java.lang.Comparablejava.util.concurrent.Callable

Some new function interfaces are added in Java 8.

java.util.function.Consumer<T>  // 消费一个元素,无返回java.util.function.Supplier<T>  // 每次返回一个 T 类型的对象java.util.function.Predicate<T> // 输入一个元素,返回 boolean 值,常用于 filterjava.util.function.Function<T,R> // 输入一个 T 类型元素,返回一个 R 类型对象

Lambda Expressions and Anonymous classes

Look at the above, there will be some people think that these features I can also be implemented using anonymous classes, what is the difference between LAMBDA expression and anonymous class? The most obvious difference is the this pointer, which represents an anonymous class in an anonymous class, and a class that contains a lambda expression in a lambda expression. At the same time, an anonymous class can implement multiple methods, whereas a LAMBDA expression has only one method.
Intuitively, many people would think that a LAMBDA expression might be just a syntactic sugar and eventually be converted to an anonymous class. In fact, given the efficiency issue, and the forward compatibility issue, Java 8 does not use anonymous class syntax sugars, nor does it use specialized function processing types to implement lambda expressions, as in other languages.

Lambda implementation

Since lambda expressions are not implemented in the same way as anonymous classes, what is the rationale behind this, when we analyzed the generics, we were parsing the bytecode, and here it is. Let's look at a bit of code and bytecode first.

public class LambdaStudy004 {    public void print() {        List<Integer> list = Arrays.asList(1, 2, 3, 4);        list.forEach(x -> System.out.println(x));    }}

Javap-p Results

public class lambda.LambdaStudy004 {  public lambda.LambdaStudy004();  public void print();  private static void lambda$print$0(java.lang.Integer);}

Obviously, after the lambda expression is compiled, a private static method of the class is generated, however, it is not so simple, although a static method is generated, what is the lambda expression itself, there is no function pointer in Java, there is always a class to call the static method as a carrier.

Javap-p-V View byte code

...37: invokedynamic #5,  0              // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;42: invokeinterface #6,  2            // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)47: return...

Unlike normal static method invocations invokestatic , the invocation of lambda expressions takes the new instruction introduced in Java 7, invokedynamic which is designed to enhance the introduction of dynamic language features in Java, which invokedynamic invoke the function dynamically when the instruction is called. metafactory Generates an object that implements the function interface, which actually invokes the previously generated static method, which is the actual translated representation of the lambda expression, and the translation code is as follows

class LambdaStudy004Inner {    private static void lambda$print$0(Integer x) {        System.out.println(x);    }    private class lambda$1 implements Consumer<Integer> {        @Override        public void accept(Integer x) {            LambdaStudy004Inner.lambda$print$0(x);        }    }    public void print() {        List<Integer> list = Arrays.asList(1, 2, 3, 4);        list.forEach(new LambdaStudy004Inner().new lambda$1());    }}

The specific introduction invokedynamic of implementing lambda expression is the reason why you can see the explanation of R big, Portal: Why is the lambda expression of Java 8 based on invokedynamic

Related Article

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.