深入浅出 Java 8 Lambda 表达式(深入浅出javaweb实战)

网友投稿 597 2022-09-12

本站部分文章、图片属于网络上可搜索到的公开信息,均用于学习和交流用途,不能代表睿象云的观点、立场或意见。我们接受网民的监督,如发现任何违法内容或侵犯了您的权益,请第一时间联系小编邮箱jiasou666@gmail.com 处理。

深入浅出 Java 8 Lambda 表达式(深入浅出javaweb实战)

从 Swing 开始,我们总是通过匿名类给方法传递函数功能,以下是旧版的事件监听代码:

someObject.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { //Event listener implementation goes here... } });

在上面的例子里,为了给 Mouse 监听器添加自定义代码,我们定义了一个匿名内部类 MouseAdapter 并创建了它的对象,通过这种方式,我们将一些函数功能传给 addMouseListener 方法。

简而言之,在 Java 里将普通的方法或函数像参数一样传值并不简单,为此,Java 8 增加了一个语言级的新特性,名为 Lambda 表达式。

为什么 Java 需要 Lambda 表达式?

如果忽视注解(Annotations)、泛型(Generics)等特性,自 Java 语言诞生时起,它的变化并不大。Java 一直都致力维护其对象至上的特征,在使用过 JavaScript 之类的函数式语言之后,Java 如何强调其面向对象的本质,以及源码层的数据类型如何严格变得更加清晰可感。其实,函数对 Java 而言并不重要,在 Java 的世界里,函数无法独立存在。

Lambda 表达式为 Java 添加了缺失的函数式编程特点,使我们能将函数当做一等公民看待。尽管不完全正确,我们很快就会见识到 Lambda 与闭包的不同之处,但是又无限地接近闭包。在支持一类函数的语言中,Lambda 表达式的类型将是函数。但是,在 Java 中,Lambda 表达式是对象,他们必须依附于一类特别的对象类型——函数式接口(functional interface)。我们会在后文详细介绍函数式接口。

Lambda 表达式简介

Java 中的 Lambda 表达式通常使用 (argument) -> (body) 语法书写,例如:

(arg1, arg2...) -> { body }(type1 arg1, type2 arg2...) -> { body }

以下是一些 Lambda 表达式的例子:

(int a, int b) -> { return a + b; }() -> System.out.println("Hello World");(String s) -> { System.out.println(s); }() -> 42() -> { return 3.1415 };

Lambda 表达式的结构

让我们了解一下 Lambda 表达式的结构。

什么是函数式接口

每个 Lambda 表达式都能隐式地赋值给函数式接口,例如,我们可以通过 Lambda 表达式创建 Runnable 接口的引用。

Runnable r = () -> System.out.println("hello world");

当不指明函数式接口时,编译器会自动解释这种转化:

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

因此,在上面的代码中,编译器会自动推断:根据线程类的构造函数签名 public Thread(Runnable r) { },将该 Lambda 表达式赋给 Runnable 接口。

以下是一些 Lambda 表达式及其函数式接口:

Consumer c = (int x) -> { System.out.println(x) };BiConsumer b = (Integer x, String y) -> System.out.println(x + " : " + y);Predicate p = (String s) -> { s == null };

以下是一种自定义的函数式接口: @FunctionalInterface public interface WorkerInterface {

public void doSomeWork();}

根据定义,函数式接口只能有一个抽象方法,如果你尝试添加第二个抽象方法,将抛出编译时错误。例如:

@FunctionalInterfacepublic interface WorkerInterface { public void doSomeWork(); public void doSomeMoreWork();}

错误:

Unexpected @FunctionalInterface annotation @FunctionalInterface ^ WorkerInterface is not a functional interface multiple non-overriding abstract methods found in interface WorkerInterface 1 error

函数式接口定义好后,我们可以在 API 中使用它,同时利用 Lambda 表达式。例如:

输出:

Worker invoked using Anonymous class Worker invoked using Lambda expression

这上面的例子里,我们创建了自定义的函数式接口并与 Lambda 表达式一起使用。execute() 方法现在可以将 Lambda 表达式作为参数。

Lambda 表达式举例

学习 Lambda 表达式的最好方式是学习例子。

线程可以通过以下方法初始化:

//旧方法:new Thread(new Runnable() {@Overridepublic void run() { System.out.println("Hello from thread");}}).start();//新方法:new Thread(() -> System.out.println("Hello from thread")).start();

事件处理可以使用 Java 8 的 Lambda 表达式解决。下面的代码中,我们将使用新旧两种方式向一个 UI 组件添加 ActionListener:

//Old way:button.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) { System.out.println("The button was clicked using old fashion code!");}});//New way:button.addActionListener( (e) -> { System.out.println("The button was clicked. From Lambda expressions !");});

以下代码的作用是打印出给定数组中的所有元素。注意,使用 Lambda 表达式的方法不止一种。在下面的例子中,我们先是用常用的箭头语法创建 Lambda 表达式,之后,使用 Java 8 全新的双冒号(::)操作符将一个常规方法转化为 Lambda 表达式:

//Old way:List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);for(Integer n: list) { System.out.println(n);}//New way:List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);list.forEach(n -> System.out.println(n));//or we can use :: double colon operator in Java 8list.forEach(System.out::println);

在下面的例子中,我们使用断言(Predicate)函数式接口创建一个测试,并打印所有通过测试的元素,这样,你就可以使用 Lambda 表达式规定一些逻辑,并以此为基础有所作为:

import java.util.Arrays;import java.util.List;import java.util.function.Predicate;public class Main {public static void main(String [] a) { List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); System.out.println("Print all numbers:"); evaluate(list, (n)->true); System.out.println("Print no numbers:"); evaluate(list, (n)->false); System.out.println("Print even numbers:"); evaluate(list, (n)-> n%2 == 0 ); System.out.println("Print odd numbers:"); evaluate(list, (n)-> n%2 == 1 ); System.out.println("Print numbers greater than 5:"); evaluate(list, (n)-> n > 5 );}public static void evaluate(List list, Predicate predicate) { for(Integer n: list) { if(predicate.test(n)) { System.out.println(n + " "); } }}}

输出:

Print all numbers: 1 2 3 4 5 6 7 Print no numbers: Print even numbers: 2 4 6 Print odd numbers: 1 3 5 7 Print numbers greater than 5: 6 7

//Old way:List list = Arrays.asList(1,2,3,4,5,6,7);for(Integer n : list) { int x = n * n; System.out.println(x);}//New way:List list = Arrays.asList(1,2,3,4,5,6,7);list.stream().map((x) -> x*x).forEach(System.out::println);

下面的例子会计算给定数值中每个元素平方后的总和。请注意,Lambda 表达式只用一条语句就能达到此功能,这也是 MapReduce 的一个初级例子。我们使用 map() 给每个元素求平方,再使用 reduce() 将所有元素计入一个数值:

//Old way:List list = Arrays.asList(1,2,3,4,5,6,7);int sum = 0;for(Integer n : list) { int x = n * n; sum = sum + x;}System.out.println(sum);//New way:List list = Arrays.asList(1,2,3,4,5,6,7);int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();System.out.println(sum);

Lambda 表达式与匿名类的区别

使用匿名类与 Lambda 表达式的一大区别在于关键词的使用。对于匿名类,关键词 this 解读为匿名类,而对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类。

到此为止啦,亲们!

上一篇:Carol Carpenter:DevOps 的真正价值在哪?(carol是男名还是女名)
下一篇:电商搜索引擎的架构设计和性能优化(电商搜索引擎的架构设计和性能优化论文)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~