比如下面的代码:
// 这里调用 new Cat() Supplier<Cat> constructRef1 = Cat::new; // 这里调用 new Cat(Integer) Function<Integer, Cat> constructRef2 = Cat::new; 9. lambda表达式中引入外部变量的限制要求引入lambda表达式中的变量,必须是最终变量,即该变量不会再被修改
比如下面的代码:
public static void main(String[] args) { String str = "javalover.cc"; Runnable runnable = ()->{ str = "1";// 这里会报错,因为修改了str引用的指向 System.out.println(str); } }可以看到,lambda表达式引用了外面的str引用,但是又在表达式内部做了修改,结果就报错了
为啥要有这个限制呢?
为了线程安全,因为lambda表达式有一个好处就是只在需要的时候才会执行,而不是调用后立马执行
这样就会存在多个线程同时执行的并发问题
所以Java就从根源上解决:不让变量被修改,都是只读的
那你可能好奇,我不把str的修改代码放到表达式内部可以吗?
也不行,道理是一样的,只要lambda有用到这个变量,那这个变量不管是在哪里被修改,都是不允许的
不然的话,我这边先执行了一次lambda表达式,结果你就改了变量值,那我第二次执行lambda,不就乱了吗
10. lambda的组合操作最后是lambda的必杀技:组合操作
在这里叫组合或者复合都可以
概述:组合操作就是先用一个lambda表达式,然后再在后面组合另一个lambda表达式,然后再在后面组合另另一个lambda表达式,然后。。。有点像是链式操作
学过JS的都知道Promise,里面的链式操作就和这里的组合操作很像
用过Lombok的朋友,应该很熟悉@Builder注解,其实就是构造者模式
下面我们用代码演示下组合操作:
// 重点代码 public class ComposeDemo { public static void main(String[] args) { List<Dog> list = Arrays.asList(new Dog(1,2), new Dog(1, 1)); // 1. 先按年龄排序(默认递增) // Dog::getAge, 上面介绍的方法引用 // comparingInt, 是Comparator的一个静态方法,返回Comparator<T> Comparator<Dog> comparableAge = Comparator.comparingInt(Dog::getAge); // 2. 如果有相同的年龄,则年龄相同的再按体重排序(如果年龄已经比较出大小,则下面的体重就不会再去比较) Comparator<Dog> comparableWeight = Comparator.comparingInt(Dog::getWeight);; // 3. 调用list对象的sort方法排序,参数是Comparator<? super Dog> list.sort(comparableAge.thenComparing(comparableWeight)); System.out.println(list); } } // 非重点代码 class Dog{ private int age; private int weight; public Dog(int age, int weight) { this.age = age; this.weight = weight; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } @Override public String toString() { return "Dog{" + "age=" + age + ", weight=" + weight + '}'; } }输出:[Dog{age=1, weight=1}, Dog{age=1, weight=2}]
比较的流程如下所示:
总结
lambda的语法: 参数+符合+表达式或语句,比如(a,b)->{System.out.println("javalover.cc");}
函数式接口:只有一个抽象方法,最好加@FunctionalInterface,这样编译器可及时发现错误,javadoc也说明这是一个函数式接口(可读性)
行为参数化:就是函数式接口作为参数,然后再将lambda表达式传给函数式接口,通过不同的lambda内容实现不同的行为
方法引用:lambda的语法糖,总共有三种:
Object::instanceMethod(对象的实例方法)
Class::staticMethod(类的静态方法)
Class::instanceMethod(类的实例方法)
构造引用:就一种,编译器自己可判断是哪个构造函数,语法为Class::new
在lambda中引入外部变量,必须保证这个变量是最终变量,即不再被修改
lambda的组合操作,就是链式操作,组合是通过函数式接口的静态方法来组合(静态方法会返回另一个函数式接口的对象)
比如list.sort(comparableAge.thenComparing(comparableWeight));
后记最后,感谢大家的观看,谢谢