Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

1
2
3
(parameters) -> expression
//或
(parameters) ->{ statements; }

重要特性

可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class LambdaRelevant {
//static String n="evan";
public static void main(String[] args) {
MathOperation addition= Integer::sum;//此处用到了Java8新特性“方法引用”
//等价于MathOperation addition=(int a,int b)->a+b;

MathOperation subtraction=(int a,int b)->a-b;

MathOperation multiplication=(a,b)->{return a*b;};

MathOperation division=(a,b)->a/b;

String n="evan";
GreetingService greetingService = msg -> {
//n="Leo";
//尝试修改外部变量值报错:Variable used in lambda expression should be final or effectively final
//String n="Leo";
// 报错:Variable 'n' is already defined in the scope
System.out.println(msg+" "+n);};

LambdaRelevant lambdaRelevant = new LambdaRelevant();
System.out.println("1+1="+lambdaRelevant.operation(1,1,addition));//run result:1+1=2
System.out.println("2-1="+lambdaRelevant.operation(2,1,subtraction));//run result:2-1=1
System.out.println("2*1="+lambdaRelevant.operation(2,1,multiplication));//run result:2*1=2
System.out.println("2/2="+lambdaRelevant.operation(2,2,division));//run result:2/2=1
greetingService.message("Hello");//run result:Hello evan

//error:Incompatible parameter types in lambda expression: wrong number of parameters: expected 1 but found 0
//GreetingService greetingService1 = ()->System.out.println("Hello");

//error:Multiple non-overriding abstract methods found in interface LambdaRelevant.TalkSerivce
//TalkSerivce talkSerivce=msg->System.out.println("Hello "+msg);

}

interface MathOperation {
int operation(int a,int b);
}

@FunctionalInterface
interface GreetingService{
//抽象方法
void message(String msg);
//java.lang.Object中的方法不是抽象方法
public boolean equals(Object var);
//default不是抽象方法
public default void defaultMethod(){
System.out.println("this is default method");
}
//static不是抽象方法
public static void staticMethod(){
System.out.println("this is static method");
}
}

//@FunctionalInterface
//如果加上该注解,同样会报错:Multiple non-overriding abstract methods found in interface LambdaRelevant.TalkSerivce
interface TalkSerivce{
void Whisper(String msg);
void Loud(String msg);
}

private int operation(int a,int b,MathOperation mathOperation){
return mathOperation.operation(a,b);
}
}

方法引用

方法引用是Java8 的新特性。
我们知道,使用Lambda表达式可以极大地简化我们的代码,但有时候Lambda体中的功能已经有现成的方法实现了,这时我们可以直接使用方法引用,而不是重复地去实现该功能。

方法引用可以理解为Lambda表达式的另一种表现形式。

方法引用引用的方法的参数列表和返回值类型,必须和函数式接口中抽象方法的参数列表和返回值类型保持一致。

  1. 构造器引用:Class::new
  2. 静态方法引用:Class::static_method
  3. 特定类的任意对象的方法引用:Class::method
  4. 特定对象的方法引用:instance::method

总结

  1. 函数式接口(Functional Interface),在这个接口里面只能有一个抽象方法,这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。
  2. 关于@FunctionalInterface注解,Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
  • 该注解只能标记在”有且仅有一个抽象方法”的接口上。
  • JDK8接口中的静态方法和默认方法,都不算是抽象方法。
  • 接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么 也不算抽象方法。
    PS:该注解不是必须的,如果一个接口符合”函数式接口”定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。
  1. lambda 表达式只能引用标记了 final 的外层局部变量(final关键字可以省略),这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。(lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义))
  2. 在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

待做

  1. 了解什么是JDK8接口中的默认方法。
  2. 了解forEach。
  3. 动手用代码尝试一下方法引用。