HTML5技术

Java 8 Lambda 表达式 - Felix_ICanFixIt

字号+ 作者:H5之家 来源:H5之家 2017-04-22 17:04 我要评论( )

Lambda 是啥玩意 简单来说,Lambda 就是一个匿名的方法,就这样,没啥特别的。它采用一种非常简洁的方式来定义方法。当你想传递可复用的方法片段时,匿名方法非常有用。例如,将一个方法传递给另外一个方法。 Tips 其实很多主流语言早已支持 lambda 表达式,

Lambda 是啥玩意

简单来说,Lambda 就是一个匿名的方法,就这样,没啥特别的。它采用一种非常简洁的方式来定义方法。当你想传递可复用的方法片段时,匿名方法非常有用。例如,将一个方法传递给另外一个方法。

Tips
其实很多主流语言早已支持 lambda 表达式,例如,Scala,C#,Objective-C,Ruby,C++(11), Python等等。所以也不是啥新玩意儿。

匿名方法 VS 匿名类

需要谨记一点,在 Java 里,匿名方法和匿名类并不是相同的。匿名类仍然需要实例化对象,匿名类虽然没有明确的名字,但它只有是一个对象时才能够使用。
而匿名方法并不需要给它分配实例,方法与作用的数据分离,而对象与它所作用的数据密切相关。

Java 中的 Lambda 表达式

在 Java 8之前,一个实现了只有一个抽象方法的接口的匿名类看起来更像Lambda 表达式。下面的代码中,anonymousClass方法调用waitFor方法,参数是一个实现接口的Condition类,实现的功能为,当满足某些条件,Server 就会关闭。
下面的代码是典型的匿名类的使用。

void anonymousClass() { final Server server = new HttpServer(); waitFor(new Condition() { @Override public Boolean isSatisfied() { return !server.isRunning(); } }

下面的代码用 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变量。

public class Example { private String firstName = "Tom"; public void example() { Function<String, String> addSurname = surname -> { // equivalent to this.firstName return firstName + " " + surname; // or even, }; } }

这里的firstName就是this.firstName的简写。
但是在匿名类中,你必须显式地调用firstName,

public class Example { private String firstName = "Jerry"; public void anotherExample() { Function<String, String> addSurname = new Function<String, String>() { @Override public String apply(String surname) { return Example.this.firstName + " " + surname; } }; } } 覆盖变量

在 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 表达式的方式来实现:

Arrays.sort(numbers, (first, second) -> first.compareTo(second));

这种方式更加简洁,实际上,Java 把它当做Comparator类的实例来对待。如果我们把 sort的第二个参数从 Lambda 表达式中抽取出来,它的类型为Comparator<Integer>。

Comparator<Integer> ascending = (first, second) -> first.compareTo(second); Arrays.sort(numbers, ascending); 语法分解

你可以把单一的抽象方法转换成 Lambda 表达式。
举例,如果我们有一个接口名为Example,里面只有一个抽象方法apply,该抽象方法返回某一类型。

interface Example { R apply(A args); }

我们可以匿名实现此接口里的方法:

new Example() { @Override public R apply(A args) { body } };

转换成 Lambda 表达式的话,我们去掉实例和声明,去掉方法的细节,只保留方法的参数列表和方法体。

(args) { body }

我们引入新的符号(->)来表示 Lambda 表达式。

(args) -> { body }

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • C# 快速高效率复制对象另一种方式 表达式树 - Emrys5

    C# 快速高效率复制对象另一种方式 表达式树 - Emrys5

    2017-04-06 14:00

  • Omi v1.0.2发布 - 正式支持传递javascript表达式 - 【当耐特】

    Omi v1.0.2发布 - 正式支持传递javascript表达式 - 【当耐特】

    2017-03-22 11:03

  • JavaWeb与Asp.net工作原理比较分析 - 社会主义接班人

    JavaWeb与Asp.net工作原理比较分析 - 社会主义接班人

    2017-03-12 14:00

  • 一道面试题引发的对javascript类型转换的思考 - ChokCoco

    一道面试题引发的对javascript类型转换的思考 - ChokCoco

    2017-03-06 17:00

网友点评
a