Java 8允许我们将类型为某种“特殊的接口”(即函数式接口)的匿名对象(即Lambda表达式)作为参数传入方法,这个对象是一个表达式,是对函数式接口接口声明的方法的实现,它会在某一时刻被程序调用,方法的实现(和类的实例化)发生在定义这个接口的对象时。
函数式接口必须有且只有一个未实现的方法,它的实现交由lambda表达式进行;如接口内有其他方法,必须将其他的方法声明为默认的。
在Java8前,我们使用命令式编程实现“做某件事”的动作为两个步骤:
一、创建
声明并定义方法,即描述方法签名、方法实现细节、返回值等
二、调用
我们在某个时机自行调用声明好的方法,并根据方法参数列表传递参数和接收返回值
而Java 8允许我们将某个包含唯一未实现的方法的接口的类型的对象(Lambda)作为参数传递,我们可以并且必须在调用该方法时实现传入的接口中的唯一未实现方法,这个方法的实现时机由“创建时”变为“调用时” ,这个含一个默认方法的特殊接口就是“函数式接口”
直观上看,Java8中的Lambda的作用相对与匿名内部类很相似,都可以同时声明和实例化一个类但事实上Jvm对两者的处理方式大不相同:
对于匿名内部类:
Java编译器会为每个匿名内部类创建一个新的.class文件,如果大量使用匿名内部类则生成的字节码会对直接影响系统的启动性能,而从生成的字节码来看,在执行到匿名内部类的地方程序都会创建新对象,这个创建的对象在将来某个时刻又会被销毁,这些幕后的“额外工作”都会对系统运行造成严重负担。
对于Lambda表达式:
程序使用InvokeDynamic指令来处理Lambda表达式,这个指令本来是JVM用来支持动态类型语言的,此时它被用于解析和执行Java的Lambda。这个指令与JVM上原有的4个方法执行指令(InvokeVirtual、invokeSpecial、invokeInteface、invokeStatic)一样,将承担Lambda的执行,它将Lambda的字节码转换延时到了运行时,在首次执行时该指令将查询一个由语言自身实现的bootstrap方法获取链接调用点(真正的调用发生处),后续调用无需获取新的链接,所以Lambda的解析和执行没有额外的性能损耗(如创建、销毁对象),可以放心使用。
另外,Lambda本身不带有状态,不同线程对同一个被声明为成员变量的Lambda调用不会产生线程安全问题