简单来说,Lambda 就是一个匿名的方法,就这样,没啥特别的。它采用一种非常简洁的方式来定义方法。当你想传递可复用的方法片段时,匿名方法非常有用。例如,将一个方法传递给另外一个方法。
Tips
其实很多主流语言早已支持 lambda 表达式,例如,Scala,C#,Objective-C,Ruby,C++(11), Python等等。所以也不是啥新玩意儿。
需要谨记一点,在 Java 里,匿名方法和匿名类并不是相同的。匿名类仍然需要实例化对象,匿名类虽然没有明确的名字,但它只有是一个对象时才能够使用。
而匿名方法并不需要给它分配实例,方法与作用的数据分离,而对象与它所作用的数据密切相关。
在 Java 8之前,一个实现了只有一个抽象方法的接口的匿名类看起来更像Lambda 表达式。下面的代码中,anonymousClass方法调用waitFor方法,参数是一个实现接口的Condition类,实现的功能为,当满足某些条件,Server 就会关闭。
下面的代码是典型的匿名类的使用。
下面的代码用 Lambda 表达式实现相同的功能:
void closure() { Server server = new HttpServer(); waitFor(() -> !server.isRunning()); }其实,上面的waitFor方法,更接近于下面的代码的描述:
class WaitFor { static void waitFor(Condition condition) throws InterruptedException { while (!condition.isSatisfied()) Thread.sleep(250); } } 一些理论上的区别实际上,上面的两种方法的实现都是闭包,后者的实现就是Lambda 表示式。这就意味着两者都需要持有运行时的环境。在 Java 8 之前,这就需要把匿名类所需要的一切复制给它。在上面的例子中,就需要把 server 属性复制给匿名类。
因为是复制,变量必须声明为 final 类型,以保证在获取和使用时不会被改变。Java 使用了优雅的方式保证了变量不会被更新,所以我们不用显式地把变量加上 final 修饰。
Lambda 表达式则不需要拷贝变量到它的运行环境中,从而 Lambda 表达式被当做是一个真正的方法来对待,而不是一个类的实例。
Lambda 表达式不需要每次都要被实例化,对于 Java 来说,带来巨大的好处。不像实例化匿名类,对内存的影响可以降到最小。
总体来说,匿名方法和匿名类存在以下区别:
一些具体的区别匿名方法和匿名类有一些具体的区别,主要包括获取语义和覆盖变量。
获取语义this 关键字是其中的一个语义上的区别。在匿名类中,this 指的是匿名类的实例,例如有了内部类为Foo$InnerClass,当你引用内部类闭包的作用域时,像Foo.this.x的代码看起来就有些奇怪。
在 Lambda 表达式中,this 指的就是闭包作用域,事实上,Lambda 表达式就是一个作用域,这就意味着你不需要从超类那里继承任何名字,或是引入作用域的层级。你可以在作用域里直接访问属性,方法和局部变量。
例如,下面的代码中,Lambda 表达式可以直接访问firstName变量。
这里的firstName就是this.firstName的简写。
但是在匿名类中,你必须显式地调用firstName,
在 Lambda 表达式中,
public class ShadowingExample { private String firstName = " Tim"; public void shadowingExample(String firstName) { Function<String, String> addSurname = surname -> { return this.firstName + " " + surname; }; } }因为 this 在Lambda 表达式中,它指向的是一个封闭的作用域,所以this.firstName对应的值是“Tim”,而不是跟它同名的参数的值。如果去掉this,那么引用的则是方法的参数。
在上面的例子中,如果用匿名类来实现的话,firstName指的就是方法的参数;如果想访问最外面的firstName,则使用Example.this.firstName。
public class ShadowingExample { private String firstName = "King"; public void anotherShadowingExample(String firstName) { Function<String, String> addSurname = new Function<String, String>() { @Override public String apply(String surname) { return firstName + " " + surname; } }; } } Lambda 表达式基本语法Lambda 表达式基本上就是匿名函数块。它更像是内部类的实例。例如,我们想对一个数组进行排序,我们可以使用Arrays.sort方法,它的参数是Comparator接口,类似于下面的代码。
Arrays.sort(numbers, new Comparator<Integer>() { @Override public int compare(Integer first, Integer second) { return first.compareTo(second); } });参数里的Comparator实例就是一个抽象片段,本身没有别的。在这里只有在 sort 方法中被使用。
如果我们用新的语法来替换,用 Lambda 表达式的方式来实现:
这种方式更加简洁,实际上,Java 把它当做Comparator类的实例来对待。如果我们把 sort的第二个参数从 Lambda 表达式中抽取出来,它的类型为Comparator<Integer>。
Comparator<Integer> ascending = (first, second) -> first.compareTo(second); Arrays.sort(numbers, ascending); 语法分解你可以把单一的抽象方法转换成 Lambda 表达式。
举例,如果我们有一个接口名为Example,里面只有一个抽象方法apply,该抽象方法返回某一类型。
我们可以匿名实现此接口里的方法:
new Example() { @Override public R apply(A args) { body } };转换成 Lambda 表达式的话,我们去掉实例和声明,去掉方法的细节,只保留方法的参数列表和方法体。
(args) { body }我们引入新的符号(->)来表示 Lambda 表达式。
(args) -> { body }