lambda表达式
基本语法
([参数类型] [参数名称], [参数类型] [参数名称], …) -> {lambda表达式主体}
例如
- (int a, int b) -> {return a + b;}
- () -> System.out.println(“Hello World”);
- (String s) -> {System.out.println(s);}
- () -> 43
- () -> { return 3.1415; }
注
lambda 表达式是一种匿名函数,没有访问修饰符、没有返回类型、没有方法名称
lambda表达式的结构说明
- 一个lambda可以有0个或多个参数
- 参数类型可以明确申明,也可以根据上下文类推断。例如(int a)与(a)的效果是相同的
- 所有参数需要包含在圆括号内,多个参数之间用逗号分隔。例如(a, b)或(int a, int b)或(String a, String b)
- 空圆括号代表没有参数。例如 () -> 43
- 当只有一个参数,且其类型可以推导时,()可以省略。例如 a -> a * a
- lambda 表达式的主体可以包含0条或多条语句
- 如果lambda表达式的主体只有一条语句,花括号{}可以省略,匿名函数的返回类型与该主体表达式一致
- 如果lambda表达式的主体包含一条以上的语句,则表达式必须放在花括号{}内,形成代码块,匿名函数的返回类型和代码块的返回类型一致,若没有返回则为空
函数式接口
函数式接口的定义
- 函数式接口是只包含一个抽象方法申明的接口
从java8开始支持在接口中定义默认方法(用关键字default修饰),接口中定义的默认方法具有方法体,因此接口中的默认方法不属于抽象方法。同样java8中新增的静态接口方法也不属于抽象方法。例如java8中的Function接口,只有apply一个抽象方法
@FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() { return t -> t; } }
如果一个接口申明的一个抽象方法重写了Object类中的某个public方法,那么在判断该接口是否为函数式接口时不会将该抽象方法记为该接口的抽象方法,如下所示Test 接口仍然是函数式接口
interface Test{ void test(); /** * toString和equal和Object中的申明一样, * 但java编译器在判断Test接口是否为函数式接口 * 时不会将toString和equal计为Test接口的抽象方法, * 因此接口Test只有test一个抽象方法,仍然是函数式接口 */ String toString(); boolean equal(); }
FunctionInterface注解
FunctionInterface注解用来标识一个接口为函数式接口,如果该接口不满足函数式接口的定义,那么编译器将会报错。但一个函数式接口不一定要用FunctionInterface注解来修饰,只要该接口符合函数式接口的定义,编译器仍会将其看作是函数式接口。java8中增加的函数式接口都定义在java.lang.util.function包下。
函数式接口的实例化
可以通过lambda表达式和方法引用来实例化函数式接口,只要lambda表达式或方法引用符合函数式接口中申明抽象方法,如下示例掩饰如何使用lambda表达式来实例化函数式接口,方法引用将在第三部分说明:
/**
* 定义MyInterface函数式接口,
*/
@FunctionalInterface
public interface MyInterface {
void test();
}
/**
* 测试类
*/
public class Test2 {
/**
* myTest方法接受一个MyInterface接口类型的变量
* @param myInterface
*/
public void myTest(MyInterface myInterface, String name){
myInterface.test(name);
}
public static void main(String[] args) {
Test2 test2 = new Test2();
/**
* 使用匿名内部类方式给myTest方法传参
*/
test2.myTest(new MyInterface() {
@Override
public void test(String name) {
System.out.println("Hello " + name);
}
}, "zhangsan");
/**
* 使用lambda表达式方式给myTest方法传参
*/
test2.myTest(name->{
System.out.println("Welcome " + name);
}, "lisi");
/**
* lambda表达式本质上就是函数式接口的一个实现类的对象,
* 该lambda表达式接受一个name参数,没有返回值,正好和
* myTest方法的申明吻合
*/
MyInterface myInterface = (name) -> {
System.out.println("你好 " + name);
};
test2.myTest(myInterface, "wangwu");
System.out.println(myInterface);
}
}
输出结果如下所示:
Hello zhangsan
Welcome lisi
你好 wangwu
com.ctrip.flight.test.jdk8.lambdaAndFunctionInterface.Test2$$Lambda$2/764977973@16b98e56
方法引用
方法引用可以看作是一种函数指针,是lambda表达式的语法糖,如果一个类中的某个方法正好符合lambda表达式的定义(方法的申明、方法实现的功能等),那么就可以使用方法引用来替换lambda表达式以使代码变得更加简洁。
方法引用的形式
方法引用的标准形式是 类名(或实例名)::方法名 , 方法可能是静态方法或实例方法,总共有四种形式:
类型 | 示例 |
---|---|
引用静态方法 | ClassName::staticMethodName |
引用某个对象的实例方法 | instanceName::instanceMethodName |
引用某个类型的任意对象的实例方法 | ClassName::instanceMethodName |
引用构造方法 | ClassName:new |
方法引用使用示例
下面通过一个demo来分析这几种类型方法引用:
public class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
public static int compareStudentByScore(Student student1, Student student2){
return student1.score - student2.score;
}
public static int compareStudentByName(Student student1, Student student2){
return student1.name.compareToIgnoreCase(student2.name);
}
public int compareByScore(Student other){
return this.score - other.score;
}
public int compareByName(Student other){
return this.name.compareToIgnoreCase(other.name);
}
}
public class StudentComparator{
public int compareStudentByName(Student student1, Student student2){
return student1.getName().compareToIgnoreCase(student2.getName());
}
public int compareStudentByScore(Student student1, Student student2){
return student1.getScore() - student2.getScore();
}
}
引用静态方法
public class MethodReferenceTest{
public static void main(String[] args) {
Student student1 = new Student("zhangsan", 10);
Student student2 = new Student("lisi", 90);
Student student3 = new Student("wangwu", 50);
Student student4 = new Student("zhaoliu", 40);
List<Student> students = Arrays.asList(student1, student2, student3, student4);
/**
* 使用lambda表达式形式对students按照名字进行排序
*/
students.sort((studentParam1, studentParam2) -> Student.compareStudentByName(studentParam1, studentParam2));
System.out.println(students);
/**
* 使用静态方法引用形式对students按照名字进行排序
* compareStudentByName方法的定义和lambda表达
* 式刚好吻合,因此可以用已经定义好的compareStudentByName
* 方法替换lambda表达式
*/
students.sort(Student::compareStudentByName);
System.out.println(students);
}
}
引用某个对象的实例方法
StudentComparator studentComparator = new StudentComparator();
/**
* 使用lambda表达式形式
*/
students.sort((studentParam1, studentParam2) -> studentComparator.compareStudentByName(studentParam1, studentParam2));
students.forEach(student -> System.out.println(student.getName()));
/**
* 使用 实例名::实例方法名
*/
students.sort(studentComparator::compareStudentByName);
students.forEach(student -> System.out.println(student.getName()));
引用某个类型的任意对象的实例方法
students.sort(Student::compareByScore);
students.forEach(student -> System.out.println(student.getScore()));
students.sort(Student::compareByName);
students.forEach(student -> System.out.println(student.getName()));
引用构造方法
public class MethodReferenceTest{
public String getString(Supplier<String> supplier){
return supplier.get() + "test";
}
public String getString(String string, Function<String, String> function){
return function.apply(string);
}
public static void main(String[] args) {
MethodReferenceTest methodReferenceTest = new MethodReferenceTest();
System.out.println(methodReferenceTest.getString(String::new));
System.out.println(methodReferenceTest.getString("hello", String::new));
}
}