跳至主要內容

Lambda表达式

Mr.Hope...大约 3 分钟

Lambda表达式

公式

//方法体是多行
()-> {
    <语句>;
}
//方法体内是单行,语句后不需要分号
()-><语句>

@FunctionalInterface注解

  • 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
  • image-20240412150055773
练习1 线程创建
public class LambdaLearn {
    public static void main(String[] args) {
        new Thread(()-> System.out.println("hello")).start();
    }
}
练习2 编写接口
public class LambdaLearn {
    public static void main(String[] args) {
        speak(()-> {
            System.out.println("用户1说话");
            System.out.println("用户1一直说");
        });
        speak(()-> System.out.println("用户2说话"));
    }
    public static void speak(Human human){
        human.speak();
    }
}
@FunctionalInterface
interface Human{
    public void speak();
}
练习3 排序
public class LambdaLearn {
    public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("周杰伦", 33, 175));
        list.add(new Person("刘德华", 43, 185));
        list.add(new Person("周星驰", 38, 177));
        list.add(new Person("郭富城", 23, 170));
        //按照身高逆序
        Collections.sort(list,(p1,p2)-> p2.getAge()-p1.getAge());
        for (Person person : list) {
            System.out.println(person);
        }
    }

}
@Data
@AllArgsConstructor
@NoArgsConstructor
 class Person {
    private String name;
    private int age;
    private Integer height;
 }
image-20240412145741311
/*
sort排序参数中的Comparator接口:
有两个抽象方法是因为:
equals() 方法并不算是函数式接口的一部分。在Java中,Object 类中的 equals() 方法是一个非常特殊的方法,它用于比较对象的相等性。因为 equals() 方法是 Object 类的一个公共方法,所以它并不会影响接口是否被标记为函数式接口。
*/
@FunctionalInterface
public interface Comparator<T> {
	int compare(T o1, T o2);
	boolean equals(Object obj);//公共方法,不影响注解
}

省略写法

  1. 小括号内的参数类型可以省略

  2. 如果小括号内有且仅有一个参数,则小括号可以省略

  3. 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。

内部原理

匿名内部类在编译的时候会产生一个class文件。

Lambda表达式在程序运行的时候会形成一个类。

  1. 在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的代码

  2. 还会形成一个匿名内部类,实现接口,重写抽象方法

  3. 在接口中重写方法会调用新生成的方法

使用前提

Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意

  1. 方法的参数或局部变量类型必须为接口才能使用Lambda

  2. 接口中有且仅有一个抽象方法(@FunctionalInterface)

与匿名内部类的对比

  1. 所需类型不一样

匿名内部类的类型可以是 类,抽象类,接口

Lambda表达式需要的类型必须是接口

  1. 抽象方法的数量不一样

匿名内部类所需的接口中的抽象方法的数量是随意的

Lambda表达式所需的接口中只能有一个抽象方法

  1. 实现原理不一样

匿名内部类是在编译后形成一个class

Lambda表达式是在程序运行的时候动态生成class

作用域

访问局部变量

可以直接在 lambda 表达式中访问外部的局部变量:

final int num = 1;
Converter<Integer, String> stringConverter = 
    	(from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

但是和匿名对象不同的是,这里的变量 num 可以不用声明为 final,该代码同样正确:

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

不过这里的 num 必须不可被后面的代码修改(即隐性的具有 final 的语义),例如下面的就无法编译:

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
num = 3;//在lambda表达式中试图修改num同样是不允许的。

访问字段和静态变量