Lambda表达式
Lambda表达式
函数式编程思想
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”。
面向对象思想强调“必须通过对象的形式来做事情”
函数式思想则尽量忽略面向对象的复杂语法:
强调做什么,而不是以什么形式去做“
而Lamnda表达式就是函数式思想的体现
体验Lambda表达式
需求:启动一个线程,在控制台输出一句话:多线程启动了
方式1:
- 定义一个类MyRunnable实现Runnable接口,重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable的对象作为参数传递
- 启动线程
方式2:
- 匿名内部类的方式改进
方式3:
- Lambda表达式的方式改进
Lambda表达式的标准格式
匿名内部类中重写run()方法的代码分析
- 方法形式参数为空,说明调用方法参数为空
- 方法返回值类型为void,说明方法执行没有结果返回
- 方法体中的内容,是我们具体要做的事情
new Thread(new Runnable(){
@Override
public void run(){
System.out.println("多线程启动了");
}
}).start();
Lambda表达式的代码分析
-
():里面没有内容,可以看成是方法参数为空
-
->:用箭头指向后面要做的事情
-
{}:包含一段代码,我们称之为代码块,可以看成是方法体中的内容
new Thread(()->{
System.out.println("多线程程序启动了");
}).start();
组成Lambda表达式的三要素:形式参数,箭头,代码块
Lambda表达式的格式
-
格式:(形式参数) -> {代码块}
-
形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
-
->:由英文中画线和大于符号组成,固定写法。代表指向动作
-
代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
Lambda表达式的使用前提
- 有一个接口
- 接口中有且仅有一个抽象方法
Lambda表达式的省略模式
省略规则:
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
Lambda表达式的注意事项
- 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
- 必须有上下文环境,才能推导出Lambda对应的接口
- 根据局部变量的赋值得知Lambda对应的接口:
Runnable r = () - >System.out.println(“lambda表达式”) - 根据调用方法的参数得知Lambda对应的接口:
new Lambda(()->System.out.println(“Lambda表达式”)).start;
- 根据局部变量的赋值得知Lambda对应的接口:
Lambda表达式和匿名内部类的区别
- 所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
- 使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
- 实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
函数式接口
函数式接口:有且仅有一个抽象方法的接口
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以使用于Lambda使用的接口
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导
如何检测一个接口是不是函数式接口?
- @FunctionalInterface
- 放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
注意:
- 我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口,但是,建议加上该注解
函数式接口作为方法的参数
如果方法的参数是一个函数式表达式,我们可以使用Lambda表达式作为参数传递
- startThread(() - > System.out.println(Thread.currentThread().getName() + “线程启动了”))
函数是接口作为方法的返回值
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
private static Comparator<String> getComparator(){
return (s1,s2) -> s1.length()-s2.length;
}
常用函数式接口
Java 8在Java.util.function包下预定义了大量的函数式接口供我们使用
重点学习下面四个接口
- Supplier接口
- Consumer接口
- Predicate接口
- Function接口
Supplier接口
Supplier:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式返回一个数据)
- Supplier 接口也被称为生产型接口,如果我们指定了接口的泛型式什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
Consumer接口
Comsumer :包含两个方法
- void accept(T t):对给定的参数执行此操作
- default Consumer andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作
- Comsumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
Predicate接口
Predicate :常用的四个方法
-
boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
-
default Predicate negate():返回一个逻辑的否定,对应逻辑非
-
default Predicate and(Predicate other):返回一个组合判断,对应短路与
-
default Predicate or(Predicate other):返回一个组合判断,对应短路或
-
Predicate接口通常用于判断参数是否满足指定的条件
Function接口
Function<T,R>:常用的两个方法
-
R apply(T t):将此函数应用于给定的参数
-
default Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after应用于结果
-
Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现,然后返回一个新的值)