Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
Lambda表达式的格式
->
:lambda操作符或箭头操作符
操作符的左边:lambda形参列表,对应着要重写的接口中的抽象方法的形参列表
操作符的右边:lambda体,对应着接口的实现类或要重写的方法的方法体
具体格式如下:
形参列表 -> lambda体
无参无返回值
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("r1");
}
};
r1.run();
System.out.println("****************************");
Runnable r2 = () -> {
System.out.println("r2");
};
r2.run();
需要一个参数,但是没有返回值
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");
数据类型可以省略
数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con2.accept("con2");
若只需要一个参数时,参数的小括号可以省略
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("con2");
需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println(com1.compare(11, 12));
System.out.println("**************************");
Comparator<Integer> com2 = (o1, o2) -> {
return Integer.compare(o1, o2);
};
System.out.println(com2.compare(33, 33));
当Lambda体只有一条语句时,return与大括号若有,都可以省略
Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(com2.compare(33, 33));
函数式接口
只包含一个抽象方法的接口,称为函数式接口。
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
我们可以在一个接口上使用@FunctionalInterface
注解,这样做可以检查它是否是一个函数式接口。同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function
包下定义了Java 8 的丰富的函数式接口。
Java 内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer<T> 消费型接口 | T | void | 对类型为T 的对象应用操作,包含方法:void accept(T t) |
Supplier<T> 供给型接口 | 无 | T | 返回类型为T 的对象,包含方法:T get() |
Function<T, R> 函数型接口 | T | R | 对类型为T 的对象应用操作,并返回结果。结果是R 类型的对象。包含方法:R apply(T t) |
Predicate<T> 断定型接口 | T | boolean | 确定类型为T 的对象是否满足某约束,并返回boolean 值。包含方法:boolean test(T t) |
自定义函数式接口
/**
* 自定义函数式接口:接口中只有一个抽象方法
*/
@FunctionalInterface
public interface MyInterface {
void show();
}
方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向 一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
格式:使用操作符 “::
” 将类(或对象) 与方法名分隔开来。
如下三种主要使用情况:
对象::实例方法名
类::静态方法名
类::实例方法名
情况一
要求:函数式接口中的抽象方法a与其内部实现时调用的对象的某个方法b的形参列表和返回值类型都相同(或一致)。此时,可以考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用。
注意:此方法b是非静态的方法,需要对象调用。
对象::实例方法名
举例
// 匿名内部类
Consumer<String> con1 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con1.accept("con1");
// Lambda表达式
Consumer<String> con2 = s -> System.out.println(s);
con2.accept("con2");
// 方法引用
Consumer<String> con3 = System.out::println;
con3.accept("con3");
情况二
要求:函数式接口中的抽象方法a与其内部实现时调用的类的某个静态方法b的形参列表和返回值类型都相同(或一致)。此时,可以考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用。
注意:此方法b是静态的方法,需要类调用。
类::静态方法名
举例
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println(com1.compare(21, 12));
//Lambda表达式
Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(com2.compare(22, 33));
//方法引用
Comparator<Integer> com3 = Integer::compare;
System.out.println(com3.compare(33, 33));
情况三
要求:函数式接口中的抽象方法a与其内部实现时调用的对象的某个方法b的返回值类型相同。同时,抽象方法a中有n个参数,方法b中有n-1个参数,且抽象方法a的第1个参数作为方法b的调用者,且抽象方法a的后n-1个参数与方法b的n-1个参数的类型相同(或一致)。此时,可以考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用。
注意:此方法b是非静态的方法,需要对象调用。但是形式上,写成对象a所属的类
类::实例方法名
举例
Comparator<String> com1 = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
System.out.println(com1.compare("abc", "abd"));
//Lambda表达式
Comparator<String> com2 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com2.compare("abd", "abc"));
//方法引用
Comparator<String> com3 = String::compareTo;
System.out.println(com3.compare("abc", "abc"));
构造器引用
格式:ClassName::new
调用了类名对应的类中的某一个确定的构造器。具体调用的是类中的哪一个构造器取决于函数式接口的抽象方法的形参列表!
举例:
Supplier<Employee> sup1 = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println(sup1.get());
Supplier<Employee> sup2 =()->new Employee();
System.out.println(sup2.get());
Supplier<Employee> sup3 =Employee::new;
System.out.println(sup3.get());
数组引用
格式:type[] :: new
举例:
Function<Integer, String[]> func1 = new Function<Integer, String[]>() {
@Override
public String[] apply(Integer length) {
return new String[length];
}
};
System.out.println(func1.apply(10).length);
Function<Integer, String[]> func2 = length -> new String[length];
System.out.println(func2.apply(20).length);
Function<Integer, String[]> func3 = String[]::new;
System.out.println(func3.apply(30).length);