Java高级语法2:Lambda表达式
Lambda表达式
函数式编程思想概述
面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法--强调做什么,而不是以什么形式做
传递一段代码--这才是我们真正的目的。Lambda表达式是JDK1.8中最重要的新特性,它打开了函数式Java开发的大门
使用Lambda表达式实现多线程
new Thread(()->{System.out.println("新线程创建了")};).start();
匿名内部类的好处和弊端
好处:可以帮助我们省去实现类的定义
弊端:匿名内部类的语法太复杂了
Lambda表达式适用的情况
- 无参数
- 无返回值
- 代码块
在这样的情况下,用Lambda表达式更加简单
Lambda表达式的格式
Lambda表达式由参数、箭头、代码三部分组成
(参数类型 参数名称)->{代码语句}
实例
- 实例一:
题外话:用泛型构造sort函数中的Comparator

这里使用Lambda表达式可以简化:
Arrays.sort(arr,(Person o1,Person o2)->{
return o1.getAge()-o2.getAge();
});
- 实例二
有参数有返回值的情况

Lambda表达式中,凡是可以根据上下文推导得知的信息,都可以省略
- 小括号内参数的类型可以省略
- 如果小括号只有一个参数,则小括号和类型可以省略
- 如果大括号只有一个语句,则无论是否有返回值,大括号、return和语句分句都可以省略(注意这里,要是省略的话,括号、return和分号必须一起省略
举个类似的例子:

下面举一些省略的例子


上面那个例子中,参数类型、大括号、return和分号都省略了
lambda表达式的使用前提

这也可以理解,如果接口中的方法不止有一个,编译器在判断lambda表达式对应的到底是哪一个方法时就很困难
有且仅有一个抽象方法的接口,叫做函数式接口
关于Lambda表达式的详细内容,参见https://segmentfault.com/a/1190000009186509

>空括号用于表示一组空的参数。例如 () -> 42。
>
>当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a。
Lambda表达式的应用:
>线程可以初始化如下:
>
>
>
> >// Old way >new Thread(new Runnable() { > @Override > public void run() { > System.out.println("Hello world"); > } >}).start(); > >// New way >new Thread( > () -> System.out.println("Hello world") >).start(); >
>
>事件处理可以用 Java 8 使用 Lambda 表达式来完成。以下代码显示了将 ActionListener 添加到 UI 组件的新旧方式:
>
> >// Old way >button.addActionListener(new ActionListener() { > @Override > public void actionPerformed(ActionEvent e) { > System.out.println("Hello world"); > } >}); > >// New way >button.addActionListener( (e) -> { > System.out.println("Hello world"); >}); >
>
>## 6.3 遍例输出(方法引用)
>
>输出给定数组的所有元素的简单代码。请注意,还有一种使用 Lambda 表达式的方式。
>
> >// old way >List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); >for (Integer n : list) { > System.out.println(n); >} > >// 使用 -> 的 Lambda 表达式 >list.forEach(n -> System.out.println(n)); > >// 使用 :: 的 Lambda 表达式 >list.forEach(System.out::println); >
>
>## 6.4 逻辑操作
>
>输出通过逻辑判断的数据。
>
> >package com.wuxianjiezh.demo.lambda; > >import java.util.Arrays; >import java.util.List; >import java.util.function.Predicate; > >public class Main { > > public static void main(String[] args) { > List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); > > System.out.print("输出所有数字:"); > evaluate(list, (n) -> true); > > System.out.print("不输出:"); > evaluate(list, (n) -> false); > > System.out.print("输出偶数:"); > evaluate(list, (n) -> n % 2 == 0); > > System.out.print("输出奇数:"); > evaluate(list, (n) -> n % 2 == 1); > > System.out.print("输出大于 5 的数字:"); > evaluate(list, (n) -> n > 5); > } > > public static void evaluate(List<Integer> list, Predicate<Integer> predicate) { > for (Integer n : list) { > if (predicate.test(n)) { > System.out.print(n + " "); > } > } > System.out.println(); > } >} >
>
>运行结果:
>
> >输出所有数字:1 2 3 4 5 6 7 >不输出: >输出偶数:2 4 6 >输出奇数:1 3 5 7 >输出大于 5 的数字:6 7 >
>
>## 6.4 Stream API 示例
>
>java.util.stream.Stream接口 和 Lambda 表达式一样,都是 Java 8 新引入的。所有 Stream 的操作必须以 Lambda 表达式为参数。Stream 接口中带有大量有用的方法,比如 map() 的作用就是将 input Stream 的每个元素,映射成output Stream 的另外一个元素。
>
>下面的例子,我们将 Lambda 表达式 x -> x*x 传递给 map() 方法,将其应用于流的所有元素。之后,我们使用 forEach 打印列表的所有元素。
>
> >// old way >List<Integer> 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<Integer> list = Arrays.asList(1,2,3,4,5,6,7); >list.stream().map((x) -> x*x).forEach(System.out::println); >
>
>下面的示例中,我们给定一个列表,然后求列表中每个元素的平方和。这个例子中,我们使用了 reduce() 方法,这个方法的主要作用是把 Stream 元素组合起来。
>
>
>
> >// old way >List<Integer> 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<Integer> 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表达式和匿名类之间的区别
>- this 关键字。对于匿名类 this 关键字解析为匿名类,而对于 Lambda 表达式,this 关键字解析为包含写入 Lambda 的类。
>- 编译方式。Java 编译器编译 Lambda 表达式时,会将其转换为类的私有方法,再进行动态绑定。
-
双冒号(::)操作符
![1568039000939]()
函数式接口

语法糖:使用更加方便但是原理不变,便于使用
Lambda不是匿名内部类的语法糖
注意,函数式接口只限制抽象(abstract)方法只能有一个,对于其他类型的方法,比如静态方法、私有方法等没有限制
@FunctionalInterface:检测接口是否为函数式接口,如果是则可以编译成功,否则就会编译失败
函数式接口可以作为方法的参数和返回值使用
- 参数使用函数式接口

使用匿名内部类和Lambda表达式进行优化:

Lambda表达式的延迟执行



存在的问题:
使用Lambda表达式进行优化:
-
定义函数式接口
package jd; /* *@author JiaDing */ @FunctionalInterface public interface MessageBuilder { public abstract String builderMessage(); } -
调用函数式接口
package jd; /* * Lambda表达式的特点:延迟加载 * Lambda表达式的前提:存在函数式接口 */ public class Jd { // 定义一个显示日志的方法,方法的参数传递日志的等级和MessageBuilder接口 public static void showLog(int level, MessageBuilder mb) { if (level == 1) { System.out.println(mb.builderMessage()); } } public static void main(String[] args) { String msg1 = "Hello"; String msg2 = "World"; String msg3 = "Java"; showLog(1, () -> { // 返回一个拼接好的字符串 return msg1 + msg2 + msg3; }); } }![1568513889328]()
这里和直接传递参数相比的区别在于,这里实际上是实现了函数式接口中的方法并在方法的return中返回拼接,如果条件不满足,那么这个方法就不会被调用,字符串自然也不会拼接了
实例1:




浙公网安备 33010602011771号