Lambda 是一个 匿名函数,我们可以把 Lambda 表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
函数式接口
Lambda表达式的本质:作为函数式接口的实例,先来看一下什么是函数式接口:只包含一个抽象方法的接口,称为函数式接口。
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
Java 内置四大核心函数式接口
Lambda 使用举例
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。这个定义够大的,让我们慢慢道来。
- 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
- 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
- 简洁——无需像匿名类那样写很多模板代码。
语法格式1:无参,无返回值,lambda表达式作为接口的实例
@Test
public void test1() {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("测试1...");
}
};
r1.run();
System.out.println("************************************");
Runnable r2 = () -> System.out.println("测试2...");
r2.run();
}
语法格式2:Lambad 需要一个参数,但是没有返回值
@Test
public void test3() {
Consumer<String> con1 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con1.accept("con1测试...");
System.out.println("********************************");
Consumer<String> con2 = (String s) -> { System.out.println(s); };
con2.accept("con2测试...");
}
语法格式3: 数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
@Test
public void test4() {
Consumer<String> con3 = (String s) -> { System.out.println(s); };
con3.accept("con3测试...");
// String 不用写了, 如果只有一个参数小括号也可以省略掉的
Consumer<String> con4 = (s) -> { System.out.println(s); };
con4.accept("con4测试...");
}
语法格式4: Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值, 那个大括号不能省略
@Test
public void test5() {
// 语法格式5: Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值, 那个大括号不能省略
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
return Integer.compare(o1, o2);
}
};
System.out.println(com1.compare(1, 2));
System.out.println("**********************************");
Comparator<Integer> com2 = (o1, o2) -> {
System.out.println(o1);
return Integer.compare(o1, o2);
};
System.out.println(com1.compare(1, 2));
System.out.println("**********************************");
// 当Lambda 体只有一条语句时,return与大括号若有,都可以省略
Comparator<Integer> com3 = (o1, o2) -> o1.compareTo(o2);
System.out.println(com1.compare(1, 2));
}
总结:
左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略
右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字
方法引用与构造器引用
方法引用
我们介绍的Java 8的第一个新功能是方法引用。比方说,你想要筛选一个目录中的所有隐藏文件。你需要编写一个方法,然后给它一个File,它就会告诉你文件是不是隐藏的。幸好,File类里面有一个叫作isHidden的方法。我们可以把它看作一个函数,接受一个File,返回一个布尔值。但要用它做筛选,你需要把它包在一个FileFilter对象里,然后传递给File.listFiles方法,如下所示:
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!使用格式: 类(或对象) :: 方法名
具体分为如下的三种情况:
- 对象 :: 非静态方法
- 类 :: 静态方法
- 类 :: 非静态方法
方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!
情况一:对象 :: 实例方法
@Test
public void test1() {
// 对象 :: 非静态方法
// Consumer中的void accept(T t)
// PrintStream中的void println(T t)
// println 这个抽象方法已经被 PrintStream 对象实现了,println 和 accept 参数列表和返回值是一致的
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("con1....");
System.out.println("*******************");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("con2....");
}
情况二:类 :: 静态方法
@Test
public void test3() {
// Comparator 中的 int compare(T t1, T t2)
// Integer 中的 int compare(T t1, T t2)
Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));
System.out.println("*******************");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12,3));
}
情况三:类 :: 实例方法
@Test
public void test5() {
// Comparator 中的 int comapre(T t1, T t2)
// String 中的 int t1.compareTo(t2)
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
System.out.println("*******************");
Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("abd","abm"));
}
@Test
public void test7() {
// Function中的R apply(T t)
// Employee中的String getName();
Employee employee = new Employee(1001, "Jerry", 23, 6000);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee));
System.out.println("*******************");
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型
// 构造器引用
// Supplier 中的 T get(): 没有参数有返回值
// Employee 的空参构造器:Employee()
@Test
public void test1(){
// 供给型函数
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("*******************");
// Lambda 形式
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("*******************");
// 构造器引用
Supplier<Employee> sup2 = Employee :: new;
System.out.println(sup2.get());
}