本文是跟着宋红康老师讲解的视频做的笔记,视频地址:[尚硅谷]Java8新特性(Lambda表达式+Stream API+Optional类)_哔哩哔哩_bilibili
目录
3. 数据类型可以省略,因为可由编译器推断得出,这称为“类型判断”
4. Lambda表达式如果只有一个参数,那么参数的小括号可以省略
6. 当Lambda方法体只有一条语句,那么return和大括号都可以省略
什么是Lambda表达式
Lambda是一个匿名函数,可以把lambda表达式理解为一段可以传递的代码
初见Lambda表达式
r1是一个Runnable接口的匿名实现类的对象,r2是使用Lambda表达式来创建对象,两个对象的效果一样,但是r2的代码明显更加简洁。这就是Lambda表达式。
化简为Lambda表达式的原则:能省就省。上图中Runnable接口只有一个需要实现的方法(即run方法),所以我们不需要写方法名就可以确定方法名(这其实就是函数式接口),由于r2是Runnable接口的对象,所以我们也可以确定类名。所以就化简为了( )→System.out.print("xxx");
再看个例子:
我们如何把上面的代码化简为Lambda表达式呢?可以看到这里仅仅多了两个参数,所以我们把两个参数加上即可:
我们还有一个更加简洁的版本:方法引用
方法引用我们后面会讲,先介绍Lambda表达式:
Lambda表达式写法
( xx,xx ) - > 方法体
左边:接口中抽象方法的形参列表
右边:重写接口中抽象方法的方法体
Lambda表达式的本质:作为接口的一个实例(对象)
表达式共有六种使用情况
1. 无参,无返回值
这是我们前面的Runnable接口的例子。
2. 一个参数,无返回值
不使用Lambda表达式:
使用Lambda表达式:
Lambda表达式的方法体中,如果只有一条语句,那么大括号写不写都行。
3. 数据类型可以省略,因为可由编译器推断得出,这称为“类型判断”
还是上面Consumer的例子,由于Consumer中的泛型为<String>,编译器可以推断出参数类型为String,所以我们不需要在Lambda表达式的形参列表中声明Stri
类型推断的另一个例子:
由于泛型中已经声明了String,我们就不需要在后面的<>中声明String了。
4. Lambda表达式如果只有一个参数,那么参数的小括号可以省略
还是Cosnumer的例子,省略小括号后:
5. Lambda有多个参数,有返回值
不使用Lambda表达式:
使用Lambda表达式:
由于类型推断,我们可以把o1和o2的类型省略。
如果实现的compare方法中有多个语句,那么我们不能省略方法体的大括号和return关键字:
6. 当Lambda方法体只有一条语句,那么return和大括号都可以省略
这就是上面的comparator例子:
总结:
Lambda表达式:(形参列表) - > { 方法体 }
本质:接口的实例对象。
**对于接口的要求:要求此接口必须只声明了一个抽象方法(这就是函数式接口)。**上面的例子中的接口,我们看源码的话会发现接口上都有一个注解@FunctionalInterface,表明这是一个函数式接口。这个注解仅仅是为了检验此接口是否为函数式接口,不加也行。
形参列表:参数类型可以省略;如果参数列表只有一个参数,那么小括号也可以省略,没有参数或者有一个以上的参数就不能省略小括号。
方法体:如果方法体只有一条执行语句,则可以省略大括号和return关键字。
函数式接口
只声明了一个抽象方法的接口。java不仅支持面向对象编程(OOP)也可以面向函数编程(OOF)
Lambda表达式就是一个函数式接口的对象。
以前用匿名实现类表示的现在都可以用Lambda表达式来写。
四大函数式接口
四大函数式接口指的是Consumer、Function、Predicate、Supplier,位于java.util.function包下
第一种:Consumer<T>:消费型接口 void accept(T t);
第二种:Supplier<T>:供给型接口 T get();
第三种:Function<T,R>:函数型接口 ,T为自变量,R为因变量 R apply(T t);
第四种:Predicate<T>:断言型接口 boolean test(T t)
消费型接口示例:
断言型接口示例:
定义一个方法,第二个参数为断言型接口,想要实现的功能为:基于某种规则pre,来筛选list中符合规则的字符串。
筛选规则我们可以通过Lambda表达式来实现:
方法引用
使用情形
当要传递给Lambda表达式方法体的操作,已经有实现的方法了,此时可以使用方法引用。
方法引用本质上就是Lambda表达式,即方法引用也是函数式接口的实例。
使用格式
类(或对象) : : 方法名
参数列表都不用写了
使用要求
对于下面的情况1和情况2:要求接口中的抽象方法的形参列表和返回值,与方法引用的方法的形参列表和返回值都相同。而且本来我们Lambda表达式的方法体代码中正好用到了方法引用的方法,我们就可以使用方法引用来替换Lambda表达式。
对于情况3,可以不满足上面的描述,具体往下看。
方法引用一共分为如下三种情况
1. 对象 : : 非静态方法
分析:Lambda表达式中的方法体为System.out.println(str),然后调用con1的accept方法。我们发现PrintStream类中的println方法和accept方法参数列表和返回值都一样,我们可以理解为println方法已经实现了accept方法(传递给Lambda表达式方法体的操作,已经有实现的方法了),所以我们可以使用方法引用
再来看一个例子:
觉得难以理解没关系,这只是一种新的语法,逻辑上确实难以理解,用多了就懂了。
2. 类 : : 静态方法
再来一个示例:
3. 类 : : 非静态方法(有难度)
我们发现compare的两个参数,第一个参数可以当做调用者调用String类的compareTo方法,第二个参数仍然作为参数。这时就可以使用类::非静态方法的形式。注意我们仍然不写compareTo的参数列表。
再看个例子:
只有一个参数的时候,也可以用类::非静态方法,apply方法唯一的一个参数当做getName方法的调用者。
构造器引用
1. 无参构造器
可以看到Employee类的无参构造方法,和get方法一样没有参数,当new 无参构造方法( )的时候会返回一个Employee对象。当我们Lambda表达式的方法体中调用了new Employee( )的时候,就可以使用方法引用Employee : : new 。
2. 有参构造器
我们同样不能在方法引用上加参数,因为参数可以通过apply传递。
再来看一个两个参数的构造器:
数组引用
返回值为一个数组对象,其他的和构造器引用类似。
把数组看做一个特殊的类,则写法与构造器引用一致。
总结
可以不会写,但是要能看懂,因为很多源码使用了Lambda表达式或者方法引用。