1、组成
组成Lambda表达式的三要素:形式参数,箭头,代码块。
Lambda表达式的格式。
格式:(形式参数) -> {代码块}。
形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可。
->:由英文中画线和大于符号组成,固定写法。代表指向动作。
代码块:是我们具体要做的事情,也就是以前我们写的方法体内容。
2、使用
Lambda表达式的使用前提:1、有一个接口。2、接口中有且仅有一个抽象方法。
练习一:不带参数
定义一个接口(Eatable),里面定义一个抽象方法:void eat();
定义一个测试类(EatableDemo),在测试类中提供两个方法
一个方法是:useEatable(Eatable e)
一个方法是主方法,在主方法中调用useEatable方法
练习二:带参数、无返回值
定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
定义一个测试类(FlyableDemo),在测试类中提供两个方法
一个方法是:useFlyable(Flyable f)
一个方法是主方法,在主方法中调用useFlyable方法
练习三:带参数、有返回值
定义一个接口(Addable),里面定义一个抽象方法:intadd(intx,inty);
定义一个测试类(AddableDemo),在测试类中提供两个方法
一个方法是:useAddable(Addable a)
一个方法是主方法,在主方法中调用useAddable方法
3、省略
省略规则:
参数类型可以省略。但是有多个参数的情况下,不能只省略一个
如果参数有且仅有一个,那么小括号可以省略
如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
4、注意事项
使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法。
必须有上下文环境,才能推导出Lambda对应的接口。
根据局部变量的赋值得知Lambda对应的接口:Runnable r = () -> System.out.println("Lambda表达式");
根据调用方法的参数得知Lambda对应的接口:new Thread(() -> System.out.println("Lambda表达式")).start();
5、Lambda表达式和匿名内部类的区别
所需类型不同
a.匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
b.Lambda表达式:只能是接口
使用限制不同
a.如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
b.如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
a.匿名内部类:编译之后,产生一个单独的.class字节码文件
b.Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
6、方法引用
解析:
方法引用符:
:: 该符号为引用运算符,而它所在的表达式被称为方法引用。
推导与省略:
a.如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导。
b.如果使用方法引用,也是同样可以根据上下文进行推导。
c.方法引用是Lambda的孪生兄弟。
7、Lambda表达式支持的方法引用
常见的引用方式:引用类方法、引用对象的实例方法、引用类的实例方法、、引用构造器。
1)引用类方法(引用类方法,其实就是引用类的静态方法)
格式:类名::静态方法
范例:Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
2)引用对象的实例方法
引用对象的实例方法,其实就引用类中的成员方法
格式:对象::成员方法
范例:"HelloWorld"::toUpperCase
String类中的方法:public String toUpperCase() 将此String所有字符转换为大写
3)引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
格式:类名::成员方法
范例:String::substring
String类中的方法:public String substring(int beginIndex,int endIndex) 从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex
4)引用构造器
引用构造器,其实就是引用构造方法
格式:类名::new
范例:Student::new
8、函数式接口
函数式接口:有且仅有一个抽象方法的接口。
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口。
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
如何检测一个接口是不是函数式接口呢?
@FunctionalInterface
放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败。
注意:
我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上该注解。
9、函数式接口作为方法的参数
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递
10、函数式接口作为方法的返回值
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
11、常用的函数式接口
java.util.function
功能接口为lambda表达式和方法引用提供目标类型。每个功能接口都有一个抽象方法,称为该功能接口的功能方法,lambda表达式的参数和返回类型与之匹配或匹配。
Supplier接口、Consumer接口、Predicate接口、Function接口。
Supplier接口
Supplier<T>:包含一个无参的方法
T get():获得结果。该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据。
Supplier<T>接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
Consumer接口
Consumer<T>:包含两个方法
void accept(T t):对给定的参数执行此操作。
default Consumer<T> andThen(Consumer after):返回一个组合的 Consumer,依次执行此操作,然后执行after操作。
Consumer<T>接口也被称为消费型接口,它消费的数据的数据类型由泛型指定。
练习:
String[]strArray= {"林青霞,30","张曼玉,35","王祖贤,33"};
字符串数组中有多条信息,请按照格式:“姓名:XX,年龄:XX"的格式将信息打印出来
要求:
把打印姓名的动作作为第一个Consumer接口的Lambda实例
把打印年龄的动作作为第二个Consumer接口的Lambda实例
将两个Consumer接口按照顺序组合到一起使用
Predicate接口
Predicate<T>:常用的四个方法
booleantest(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值。
default Predicate<T> negate():返回一个逻辑的否定,对应逻辑非。
default Predicate<T> and(Predicate other):返回一个组合判断,对应短路与。
default Predicate<T> or(Predicate other):返回一个组合判断,对应短路或。
Predicate<T>接口通常用于判断参数是否满足指定的条件。
练习:
String[]strArray= {"林青霞,30","柳岩,34","张曼玉,35","貂蝉,31","王祖贤,33"};
字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
同时满足如下要求:姓名长度大于2;年龄大于33
分析
有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断
必须同时满足两个条件,所以可以使用and方法连接两个判断条件
Function接口
Function<T,R>:(T- 函数输入的类型R- 函数结果的类型)
常用的两个方法: a. apply(T t):将此函数应用于给定的参数 b. default <V> Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果。
Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值.
练习
String s = "林青霞,30";
请按照我指定的要求进行操作:
1:将字符串截取得到数字年龄部分
2:将上一步的年龄字符串转换成为int类型的数据
3:将上一步的int数据加70,得到一个int结果,在控制台输出
请通过Function接口来实现函数拼接。