Home > 类库下载 > java类库 > A preliminary study on Java Lambda expressions

A preliminary study on Java Lambda expressions

高洛峰
Release: 2016-10-09 16:29:47
Original
1545 people have browsed it

A preliminary study on Java Lambda expressions

Foreword

This article was inspired by Trisha Gee’s keynote speech Refactoring to Java 8 at JavaOne 2016.

Java 8 has been released for more than two years, but many people are still using JDK7. For enterprises, being cautious in technology is not necessarily a bad thing, but for personal learning, if you do not learn new technologies, you are likely to be abandoned by them. An important change in Java 8 is the introduction of Lambda expression, which sounds awesome. Even though I don’t know what Lambda expression is, I still feel that it is awesome. Don't be afraid, at the language level, Lambda expression is just a new syntax. With it, Java will open the door to functional programming.

Why do you need Lambda expressions

Don’t worry about what is Lambda expression and what is functional programming. Let’s first take a look at the conveniences brought by the new syntax features of Java 8. I believe you will remember them.

Before there is a Lambda expression, to create a new thread, you need to write like this:

new Thread(new Runnable(){    @Override
    public void run(){
        System.out.println("Thread run()");
    }
}).start();
Copy after login

After there is a Lambda expression, you can write like this:

new Thread(
        () -> System.out.println("Thread run()")
).start();
Copy after login

As you can see, the previously useless template code is gone! As shown above, a common use of Lambda expressions is to replace (some) anonymous inner classes, but the role of Lambda expressions is not limited to this.

Principle of Lambda Expression

If you are new to Lambda expression, you may think it is magical: you can directly define a function without declaring the name of the class or method. But in fact, this is just a little trick provided by the compiler, and the principle behind it is not difficult to understand. The following are several possible writing forms of Lambda expressions:

Runnable run = () -> System.out.println("Hello World");// 1ActionListener listener = event -> System.out.println("button clicked");// 2Runnable multiLine = () -> {// 3
    System.out.println("Hello ");
    System.out.println("World");
};
BinaryOperator<Long> add = (Long x, Long y) -> x + y;// 4BinaryOperator<Long> addImplicit = (x, y) -> x + y;// 5
Copy after login

You can find from the above example:

Lambda expressions have types, and the left side of the assignment operation is the type. The type of the lambda expression is actually the type of the corresponding interface.

Lambda expressions can contain multiple lines of code, and you need to use curly brackets to enclose the code block, just like writing a function body.

Most of the time, the type can be omitted from the parameter list of a lambda expression, as in Listings 2 and 5. This is due to javac's type derivation mechanism, the compiler can deduce type information based on context.

In fact, each Lambda expression is the abbreviation of the original anonymous inner class, which implements a functional interface (Functional Interface). The so-called functional interface refers to an interface with the @FunctionalInterface annotation added and only one interface function inside. Java is a strongly typed language. Regardless of whether it is explicitly specified, each variable and object must have a clear type. When not explicitly specified, the compiler will try to determine the type. The type of the lambda expression is the type of the corresponding function interface.

Lambda expressions and Stream

Another important use of Lambda expressions is to use them with Stream. Stream is a sequence of elements supporting sequential and parallel aggregate operations. Stream is a sequence of elements that supports various operations on these elements, and these operations are specified through Lambda expressions. You can think of a Stream as a view of a Java Collection, just like an iterator is a view of a container (but the Stream does not modify the contents of the container). The following examples show common uses of Stream.

Example 1

Suppose you need to select a string starting with a number from a string list and output it. Before Java 7, you needed to write like this:

List<String> list = Arrays.asList("1one", "two", "three", "4four");for(String str : list){    if(Character.isDigit(str.charAt(0))){
        System.out.println(str);
    }
}
Copy after login

And in Java 8, you can write like this:

List<String> list = Arrays.asList("1one", "two", "three", "4four");
list.stream()// 1.得到容器的Steam
    .filter(str -> Character.isDigit(str.charAt(0)))// 2.选出以数字开头的字符串
    .forEach(str -> System.out.println(str));// 3.输出字符串
Copy after login

The above code first 1 . Call the List.stream() method to get the Stream of the container, 2. Then call the filter() method to filter out the string starting with a number, 3. Finally, call the forEach() method to output the result.

Using Stream has two obvious benefits:

The template code is reduced, only Lambda expressions are used to specify the required operations, and the code semantics are clearer and easier to read.

Change the external iteration to the internal iteration of Stream, which facilitates the JVM itself to optimize the iteration process (for example, it can be iterated in parallel).

Example 2

Suppose you need to select all strings that do not start with a number from a list of strings, convert them to uppercase, and put the results into a new collection. The code written in Java 8 is as follows:

List<String> list = Arrays.asList("1one", "two", "three", "4four");
Set<String> newList =
        list.stream()// 1.得到容器的Stream
        .filter(str -> !Character.isDigit(str.charAt(0)))// 2.选出不以数字开头的字符串
        .map(String::toUpperCase)// 3.转换成大写形式
        .collect(Collectors.toSet());// 4.生成结果集
Copy after login

上述代码首先1. 调用List.stream()方法得到容器的Stream,2. 然后调用filter()方法选出不以数字开头的字符串,3. 之后调用map()方法将字符串转换成大写形式,4. 最后调用collect()方法将结果转换成Set。这个例子还向我们展示了方法引用(method references,代码中标号3处)以及收集器(Collector,代码中标号4处)的用法,这里不再展开说明。

通过这个例子我们看到了Stream链式操作,即多个操作可以连成一串。不用担心这会导致对容器的多次迭代,因为不是每个Stream的操作都会立即执行。Stream的操作分成两类,一类是中间操作(intermediate operations),另一类是结束操作(terminal operation),只有结束操作才会导致真正的代码执行,中间操作只会做一些标记,表示需要对Stream进行某种操作。这意味着可以在Stream上通过关联多种操作,但最终只需要一次迭代。如果你熟悉Spark RDD,对此应该并不陌生。

结语

Java 8引入Lambda表达式,从此打开了函数式编程的大门。如果你之前不了解函数式编程,不必纠结于这个概念。编程过程中简洁明了的书写形式以及强大的Stream API会让你很快熟悉Lambda表达式的。

本文只对Java Lambda表达式的基本介绍,希望能够激发读者对Java函数式编程的兴趣。如果本文能够让你觉得Lambda表达式很好玩,函数式编程很有趣,并产生了进一步学习的欲望,那就再好不过了。文末参考文献中列出了一些有用的资源。

致谢

非常感谢阿里巴巴“西行游学计划”给予我们的支持,感谢阿里中间件团队资助我们远美国赴旧金山参加Java语言的顶级技术会议——2016 JaveOne大会。借此我们有机会跟国际顶尖大牛面对面的交流,并第一时间了解到Java语言的最新动态。同样感谢伴我一起游学的两位队友,有ta们在异国他乡的陪伴和关照,我的西行之旅才更加丰富多彩。这次游学给我们带来的惊喜,将深深留存在我们的记忆当中。

希望更多的同学能够关注并参与到阿里巴巴天池大赛当中,也希望阿里能够继续坚持对青年学生的支持和培养!

参考文献

http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
http://www.slideshare.net/trishagee/refactoring-to-java-8-devoxx-uk
《Java 8函数式编程 [英]沃伯顿》
https://www.oracle.com/javaone/speakers.html#gee


Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template