【自学】Java核心技术卷1_6.3 lambda表达式
lambda表达式:可传递的代码块,可在以后执行一次或多次
6.3.1 引入lambda的原因:Java是面向对象语言,不能直接传递代码段,必须构造一个对象,这个对象的类需要有一个方法能包含所需代码段,
6.3.2 lambda表达式语法:(参数)->{表达式}
- 如果无参数:要提供空括号 ()->{}
- 参数类型可由上下文推导:可以省略参数类型 (a,b)->{}
- 只有一个参数且其类型可由上下文推导:可以省略参数的类型以及小括号a->{}
- 无需指定lambda表达式返回类型:总可由上下文推导得出
6.3.3 functional接口:只有一个抽象方法的接口。
- 可用于创建lambda表达式、方法引用、构造器引用
- 可以声明别的default方法,因为default方法有实现,所以不是抽象方法;如果接口声明的覆盖了Object类的公开方法的方法也不计入抽象方法,因为任何接口的实现都有从Object类或者别处的实现。
- 可以在需要这种接口的变量的地方提供lambda表达式(在底层,被传递lambda表达式的方法会接收实现了函数式接口的变量,在这个变量上调用对应方法时,执行lambda表达式的体)
6.3.4 方法引用——已经有现成的方法可以完成想要传递到其他代码的某个动作
- 遇到重载方法是,编译器会从上下文找
- 可以在方法引用中使用this参数:this::equals <=> x->this.equals(x)
- super::instanceMethod:以this为目标,调用给定方法的超类版本
6.3.5 构造器引用 :ClassName::new
- 将方法引用的方法名改为new即可,编译器根据上下文判断选哪一个构造器。
- 可以用数组类型建立构造器引用:int[]::new <=> x->new int[x]
- 将数组构造器引用传入返回Object数组的方法后,此方法可以调用这个构造器得到正确类型的数组返回
6.3.6 变量作用域
- lambda表达式可以捕获外围作用域中的明确定义且不会改变的变量的值(可以不声明为final,但是后面必须不能改变变量的值)
- lambda表达式的体与嵌套块有相同的作用域,命名冲突和覆盖规则仍然适用:在lambda表达式中声明一个与局部变量同名的参数或局部变量是不合法的
- 在lambda表达式使用this时,是指创建这个lambda表达式的方法的this参数
6.3.7 处理lambda表达式
- 定义方法,方法的参数包含函数式接口变量,然后调用此方法时,给这个函数式接口变量提供lambda表达式,就可以通过此变量执行lambda表达式主体
- 如果自己设计函数式接口,则可以用@FunctionalInterface注解标记这个接口:一来可以在此接口被增加多余非抽象方法是,让编译器报错,二来在javadoc页会指出此接口是函数式接口
lambda 表达式和匿名类之间的区别
- 关键字 this:匿名类的 this 关键字指向匿名类;lambda表达式的 this 关键字指向包围lambda表达式的类。
- 编译方式:匿名内部类被编译为Outter&Inner.class文件; lambda表达式被编译为类的私有方法,使用了Java 7的 invokedynamic 字节码指令来动态绑定这个方法。
lambda表达式常用场景
- 初始化线程:new Thread(new Runnable(){})。 void run();
- 事件处理,向一个UI组件添加ActionListener:button.addActionListener(new ActionListener(){});
- 打印list所有元素:list.forEach(n->System.out.println)) (或方法引用 list.forEach(System.out::println);
- Predicate<T>创建逻辑测试:boolean test(T)
- Stream流操作:filter(Predicate);forEach(Consumer);map(Funcion);reduce(BinaryOperator)
个人理解总结:其实只要记住lambda表达式要依附函数式接口来使用,所以任何需要函数式接口变量的地方都可以赋lambda表达式,只需要根据对应的接口中的抽象方法处理好lambda的参数类型和个数,以及返回值即可(其实可以将赋给函数式接口变量的lambda表达式看作对接口抽象方法的实现体)。所以不论是上面的Stream操作还是事件处理等,只要关注函数式接口中的抽象方法即可。
java.util.function包为lambda表达式和方法引用提供了target type
代码对比:用对象的方法传递代码段、匿名内部类、lambda表达式
代码练习
import java.util.function.BiConsumer;
import java.util.function.Function;
public class LambdaTest {
final static public String str1 = "str1"; //main是static方法,所以str1要声明为static
public static void main(String args[]) {
final String str2 = "str2";
final int num = 2;
//lambda访问外部局部变量,用Runnable接口
Runnable runnable = () -> System.out.println("Runnable: " + str1 + " and " + str2);
runnable.run();
//接口变量作参数调用lambda
Function<Integer, Integer> function = n -> n + 1;
func(num, function);
func(num, n -> n * n);
//用lambda替换匿名内部类,用BiConsumer接口
BiConsumer<String, String> bio = (s1, s2) -> System.out.println("BiConsumer Lambda: " + s1 + " and " + s2);
bio.accept(str1, str2);
//匿名内部类对比
bio = new BiConsumer<String, String>() {
@Override
public void accept(String s, String s2) {
System.out.println("BiConsumer Anonymous: " + s + " and " + s2);
}
};
bio.accept(str1, str2);
}
//定义有“函数式接口变量”参数的方法
public static void func(int x, Function<Integer, Integer> function) {
System.out.println(function.apply(x));
}
}