Lambda表达式是Java 8的重要更新。它支持将代码块作为方法的参数。Lamnda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(即函数式接口)的实例。
Lambda表达式的主要作用是代替匿名内部类的烦琐语法代码。例:
有如下接口Command:
public interface Command{
void process(int[] array);
}
ProcessArray类下有方法process:
class ProcaeeArray{
public void process(int[] array, Command c){
c.process(int[] array);
}
}
由上可见:ProcessArray类的process方法需要借助Command接口来对数组array进行操作。这种需求来源于需要对数组进行不同的操作,可以使用Command接口的不同实现类来完成。这种设计方式成为命令模式。
当我们在实际使用的时候,有可能Command接口的实现类只用到一次,这时就可以使用匿名内部类来实现简化操作:
public class Test{
public static void main(String[] args){
ProcessArray pa = new ProcessArray();
int[] target = {1,2,3,4};
pa.process(target, new Command(){
int sum = 0;
for(int tmp : target)
sum +=target;
System.out.println("数组元素的和是"+sum);
});
}
}
这样我们就能通过不同的匿名内部类来对数组进行不同的操作。
而Lambda用来简化匿名内部类。以上的程序可以写成:
public class Test{
public static void main(String[] args){
ProcessArray pa = new ProcessArray();
int[] target = {1,2,3,4};
pa.process(target, (int[] array) ->{
int sum = 0;
for(int tmp : target)
sum +=target;
System.out.println("数组元素的和是"+sum);
});
}
}
由上可见:Lambda表达式由3部分组成:
1:形参列表:即例中的(int[] array)。形参列表允许省略形参类型,如果只有一个形参参数,括号也可以省略。
2:箭头:即例子中的 ->。由英文中画线和大于号组成。
3:代码块:由例子可见,代码块的内容就是原匿名内部类中方法的方法体。如果代码块只有一条语句,那么花括号可以省略。如果只有一条语句,且是return语句,那么return也可以省略,表达式自动返回值。
在此例中,Lambda表达式代表了Command实例。但在其他代码中可能需要代表Command1类型的实例、Command类型的实例。这就意味着Lambda表达式实际上会被当作类型可以随意转换的对象。到底是何种目标类型,取决于具体的代码。
但是有一点要求:即Lambda表达式的目标类型必须是:函数式接口。
函数式接口指只包含一个抽象方法的接口。Java8为函数式接口提供了@FunctionalInterface注解,它告诉编译器检查该接口必须是函数式接口,否则报错。
Lambda的代码块部分其实就对应函数式接口中的方法,形参列表部分对应方法的参数。
由于Lambda表达式当成对象,因此可以使用Lambda表达式进行赋值:
Runnable r = () -> {
System.out.println("Lambda表达式为函数式接口对象赋值");
} //Runnable为一个关于多线程的函数式接口
注意:目标对象必须是明确的函数式接口的实例。比如把Lambda赋值给Object类型的对象,会报错。这时需要强制类型转换:
Object obj = (Runnable)()->{
System.out.println("接口并没有继承Object");
} //这里需要指明一点:Runnable接口并没有继承Object类。 Lambda可以看作为一个接口的实现实例,而实例是一个类,而所有类的父类都是Object
Lambda表达式的类型可以转换,也就是相同内容的Lambda表达式可以赋值给不同的对象。
关于Lambda的简化,有方法引用和构造器引用,这里不再赘述。