Lambda表达式
Lambda 表达式为java1.8的新特性,引入的一种新的语法元素和操作符 “->”。
1.为什么使用Lambda表达式
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
以前用匿名实现类表示的现在都可以用Lambda表达式来实现
Lambda表达式的使用举例:
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
System.out.println("***********************");
Runnable r2 = () -> System.out.println("我爱北京故宫");
r2.run();
}
@Test
public void test2(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
int compare1 = com1.compare(12,21);
System.out.println(compare1);
System.out.println("***********************");
//Lambda表达式的写法
Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);
int compare2 = com2.compare(32,21);
System.out.println(compare2);
System.out.println("***********************");
//方法引用
Comparator<Integer> com3 = Integer :: compare;
int compare3 = com3.compare(32,21);
System.out.println(compare3);
}
2、Lambda表达式规则
2.1、格式
“->“ : lambda操作符,或箭头操作符
左侧:指定了 Lambda 表达式需要的参数列表 ,(其实就是接口中的抽象方法中的形参列表)(省略形参类型 为类型推断)
右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能。(其实就是重写的抽象方法的方法体)
2.2、语法格式
总结:
左边:lambda形参列表的参数类型可省略(类型推断),若lambda形参列表只用一个参数,()可省略
右边:lambda体需使用一对{}包裹,若lambda体只有一条执行语句(可能为return语句),可省略{}和return关键字。
具体语法格式有以下六种:
package com.zck.lambdatest;
import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* Lambda表达式 本质:作为接口(需为函数式接口)的具体实现对象,即作为函数式接口的实例
*
* @author zck
* @create 2020-04-19 18:37
*/
public class LambdaTest {
//语法格式一:无参,无返回值
@Test
public void test1() {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("java 使我痛苦");
}
};
r1.run();
Runnable r2 = () -> System.out.println("使我痛苦者,必使我强大");
r2.run();
}
//语法格式二:Lambda 需要一个参数,但是没有返回值。
@Test
public void test2() {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("天青色等烟雨");
Consumer<String> consumer1 = (String s) -> System.out.println(s);
// Consumer<String> consumer1 = System.out::println;
consumer1.accept("而我在等你");
}
//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
@Test
public void test3() {
Consumer<String> consumer2 = (str) -> System.out.println(str);
consumer2.accept("月色被打捞起");
}
//语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
@Test
public void test4(){
Consumer<String> consumer3 = str -> System.out.println(str);
consumer3.accept("晕开了结局");
}
//语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test5(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12, 20));
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(20, 12));
}
//语法格式六:当 Lambda 体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test6(){
Comparator<Integer> com3 = (o1,o2) -> o1.compareTo(o2);
// Comparator<Integer> com3 = Integer::compareTo;
System.out.println(com3.compare(12,12));
}
}
3、函数式(Functional)接口
定义:只包含一个抽象方法的接口,称为函数式接口。可以通过 Lambda 表达式来创建该接口的对象
可以在一个接口上使用 @FunctionalInterface 注解,检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function包下定义了Java 8 的丰富的函数式接口
Java 内置四大核心函数式接口
代码举例:
public class LambdaTest2 {
@Test
public void test1(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble);
}
});
System.out.println("********************");
happyTime(400,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money));
}
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
@Test
public void test2(){
List<String> list = Arrays.asList("北京","南京","天津","东京","西京","普京");
List<String> filterStrs = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(filterStrs);
List<String> filterStrs1 = filterString(list,s -> s.contains("京"));
System.out.println(filterStrs1);
}
//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> filterList = new ArrayList<>();
for(String s : list){
if(pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
}
4、方法引用与构造器引用
4.1、方法引用(Method References)
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式
接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
- 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的 方法的参数列表和返回值类型保持一致!
- 格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名
代码举例:
当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时:ClassName::methodName
public class MethodReferencesTest {
@Test
public void test(){
Consumer<String> con1 = s -> System.out.println(s);
con1.accept("你说嘴巴嘟嘟");
//等价于
Consumer<String> con2 = System.out :: println;
con2.accept("dududududu");
}
@Test
public void test2(){
Comparator<Integer> com1 = (x,y) -> Integer.compare(15,20);
//等价于
Comparator<Integer> com2 = Integer :: compare;
}
@Test
public void test3(){
BiPredicate<String,String> bp = (x,y) -> x.equals(y);
//等价于
BiPredicate<String,String> bp1 = String::equals;
boolean flag = bp1.test("hello", "hi");
System.out.println(flag);
}
}
4.2、构造器引用与数组引用
构造器引用格式: ClassName::new
可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象 方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
代码举例:
@Test
public void test1(){
//Supplier 的 T get();
//Person 的空参构造器 person()
Supplier<Person> sup = () -> new Person();
Person person = sup.get();
//格式:ClassName::new
Supplier<Person> sup1 = Person::new;
Person person1 = sup1.get();
System.out.println(person1);//Person{age=0, name='null'}
//Function<T, R> 的 R apply(T t)
Function<String,Person> fun = name -> new Person(name) ;
Person person2 = fun.apply("Tom");
Function<String,Person> fun1 = Person::new;
Person person3 = fun1.apply("Lucy");
System.out.println(person3);//Person{age=0, name='Lucy'}
//BiFunction<T, U, R> 的 R apply(T t, U u)
BiFunction<Integer,String,Person> biFun = (id,name) -> new Person(id,name);
Person person4 = biFun.apply(12, "Marry");
System.out.println(person4);//Person{age=12, name='Marry'}
BiFunction<Integer,String,Person> biFun1 = Person::new;
Person person5 = biFun1.apply(20, "Judy");
System.out.println(person5);//Person{age=20, name='Judy'}
}
数组引用格式: type[] :: new
代码举例:
@Test
public void test2(){
//Function<T, R> 的 R apply(T t)
Function<Integer,String[]> fun1 = length -> new String[length];
String[] arr1 = fun1.apply(5);
System.out.println(Arrays.toString(arr1));//[null, null, null, null, null]
Function<Integer,String[]> fun2 = String[]::new;
String[] arr2 = fun2.apply(5);
arr2[1] = "马云";
arr2[2] = "马化腾";
arr2[3] = "刘强东";
System.out.println(Arrays.toString(arr2));//[null, 马云, 马化腾, 刘强东, null]
}
注:本文章是根据哔哩哔哩公开课 Java -Java 学习- Java 基础到高级-宋红康-零基础自学Java-尚硅谷 整理所得
大爱康师傅!!!