Lambda 表达式是 Java 8 提供的一种语法,主要用来替代匿名内部类,传递函数式接口代码。
函数式接口:只有一个抽象方法的接口,还允许定义静态方法和默认方法。
基本语法
Lambda 表达式由
->
分隔为两部分,前面是方法的参数部分,后面
{}
内是方法的实现代码。
import java.io.File;
public class Test1 {
public static void main(String[] args) {
File file = new File(".");
File[] fileArr = file.listFiles((File dir, String name) -> {
return null != name && name.endsWith(".txt");
});
}
}
当主体代码只有一条语句的时候,
{}
括号 和
return
语句 可以省略。
此时,主体代码是一个表达式,表达式的值就是函数的返回值,结尾不能加分号
;
,也不能加
return
语句。
import java.io.File;
public class Test1 {
public static void main(String[] args) {
File file = new File(".");
File[] fileArr = file.listFiles((File dir, String name) -> null != name && name.endsWith(".txt"));
}
}
方法的参数类型声明也可以省略。
File file = new File(".");
File[] fileArr = file.listFiles((dir, name) -> null != name && name.endsWith(".txt"));
当参数只有一个的时候,参数部分的括号可以省略,参数为空或者多个的时候,括号不能省略。
File file = new File(".");
File[] files = file.listFiles(path -> path.getName().endsWith(".txt"));
Lambda 表达式通过推断的方式实现,与匿名内部类相似,它可以访问定义在主体代码外部的变量,对于局部变量,只能访问 final 类型的变量或者事实上只赋值一次的变量赋值。
File file = new File(".");
String msg = "消息";
File[] files = file.listFiles(path -> {
System.out.println(msg);
return path.getName().endsWith(".txt");
});
Java 会为每个匿名内部类生成一个类,但 Lambda 表达式不会生成类,也不会引入接口,不用担心会生成太多类的问题。
函数式接口
函数式接口:只有一个抽象方法的接口,还允许定义静态方法和默认方法。都有一个注解:@FunctionalInterface 。
Lambda 表达式可以赋值给函数式接口。
FileFilter filter = path -> path.getName().endsWith(".txt");
FilenameFilter fileNameFilter = (dir, name) -> name.endsWith(".txt");
Java 8 在 java.util.function 包中定义了大量的预定义函数式接口,用于常见类型的代码传递。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Test1 {
public static void main(String[] args) {
List<Student> students = Arrays.asList(new Student[]{
new Student("张三", 89),
new Student("李四", 94),
new Student("王五", 90)
});
students = filter(students, t -> t.getScore() >= 90);
for (Student student: students) {
System.out.println(student.name + ":" + student.getScore());
}
}
public static<E> List<E> filter(List<E> list, Predicate<E> pred) {
List<E> retList = new ArrayList<>();
for(E ele : list) {
if (pred.test(ele)) {
retList.add(ele);
}
}
return retList;
}
}
class Student {
String name;
double score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public Student(String name, double score) {
this.name = name;
this.score = score;
}
}
方法引用
Lambda 表达式经常用于调用对象的某个方法,比如:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class Test1 {
public static void main(String[] args) {
List<Student> students = Arrays.asList(new Student[]{
new Student("张三", 89),
new Student("李四", 94),
new Student("王五", 90)
});
List<String> names = getNameList(students, t -> t.getName());
for(String name: names) {
System.out.println(name);
}
}
public static<T, R> List<String> getNameList(List<T> list, Function<T, R> mapper) {
List<String> retList = new ArrayList<>(list.size());
for(T ele : list){
retList.add((String) mapper.apply(ele));
}
return retList;
}
}
class Student {
String name;
double score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public Student(String name, double score) {
this.name = name;
this.score = score;
}
public static String getCollegeName(){
return "xxx School";
}
}
对于
List<String> names = getNameList(students, t -> t.getName());
,可以进一步简化:
List<String> names = getNameList(students, Student::getName);
形如
Student::getName
称为方法引用,由
::
分隔为两部分,前面是类名或变量名,后面是方法名。
当 Lambda 表达式的操作仅仅是调用某个存在的方法时,可以使用方法引用。
此外,方法引用的方法的参数类型和返回值类型必须和接口一致。
-
类名::类方法名
Supplier<String> s1 = () -> Student.getCollegeName();
Supplier<String> s2 = Student::getCollegeName;
-
类名::实例方法名
在调用实例作为参数传入时使用。
Function<Student, String> f1 = (Student t) -> t.getName();
Function<Student, String> f2 = Student::getName;
-
实例名::实例方法名
for (Student student: students) {
Supplier<String> s2 = () -> student.getName();
Supplier<String> s1 = student::getName;
}
-
构造方法引用
对于构造方法,方法引用的语法是
<类名>::new
,比如:Student::new。
BiFunction<String, Double, Student> s1 = (name, score) -> new Student(name, score);
BiFunction<String, Double, Student> s2 = Student::new;
IntFunction<int[]> arrayMaker = int[]::new;
int[] array = arrayMaker.apply(10) // 创建数组 int[10]
参考:《Java 编程的逻辑》马俊昌