Java8 Lambda表达式

Posted ooo0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8 Lambda表达式相关的知识,希望对你有一定的参考价值。

 

一,java8为什么会出现Lambda表达式

Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常的列表,可以简洁地传递代码。

•匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
•函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
•传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
•简洁——无需像匿名类那样写很多模板代码。

比如,利用Lambda表达式,可以更为简洁地自定义一个Comparator对象。

// 使用匿名类,按照重量升序对库存排序
inventory.sort(new Comparator<Apple>() 
    public int compare(Apple a1, Apple a2) 
        return a1.getWeight().compareTo(a2.getWeight());
    
);
System.out.println(inventory);
// 用Lambda表达式,按照重量升序对库存排序
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
System.out.println(inventory);

技术图片

Lambda表达式有三个部分:

•参数列表——这里它采用了Comparator中compare方法的参数,两个Apple。
•箭头——箭头->把参数列表与Lambda主体分隔开。
•Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值了。

为了进一步说明,下面给出了Java 8中五个有效的Lambda表达式的例子。

//Java 8中有效的Lambda表达式
//第一个Lambda表达式具有一个String类型的参数并返回一个int。Lambda没有return语句,因为已经隐含了return
(String s) -> s.length() 
//第二个Lambda表达式有一个Apple 类型的参数并返回一个boolean(苹果的重量是否超过150克)
(Apple a) -> a.getWeight() > 150 
//第三个Lambda表达式具有两个int类型的参数而没有返回值(void返回)。注意Lambda表达式可以包含多行语句,这里是两行
(int x, int y) ->     
    System.out.println("Result:");
    System.out.println(x+y);

//第四个Lambda表达式没有参数,返回一个int
() -> 42 
//第五个Lambda表达式具有两个Apple类型的参数,返回一个int:比较两个Apple的重量
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

 Java语言设计者选择这样的语法,是因为C#和Scala等语言中的类似功能广受欢迎。Lambda的基本语法是(parameters) -> expression或(请注意语句的花括号)(parameters) -> statements;

 技术图片

 

 

 

 二,Lambda使用限制

Lambda表达式可以被赋给一个变量,或传递给一个接受函数式接口作为参数的方法,这个Lambda表达式的签名要和函数式接口的抽象方法一样

可能会想:“为什么只有在需要函数式接口的时候才可以传递Lambda呢?”语言的设计者也考虑过其他办法,例如给Java添加函数类型。但是他们选择了现在这种方式,因为这种方式自然且能避免语言变得更复杂。此外,大多数Java程序员都已经熟悉了具有一个抽象方法的接口的理念(例如事件处理)。 

 

附:只有一个抽象方法的接口为函数式接口,函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描述符。例如,Runnable接口可以看作一个什么也不接受什么也不返回(void)的函数的签名,因为它只有一个叫作run的抽象方法,这个方法什么也不接受,什么也不返回(void)。

 

附: @FunctionalInterface又是怎么回事? 如果去看看新的Java API,会发现函数式接口带有@FunctionalInterface的标注。这个标注用于表示该接口会设计成一个函数式接口。如果用@FunctionalInterface定义了一个接口,而它却不是函数式接口的话,编译器将返回一个提示原因的错误。例如,错误消息可能是“Multiple nonoverriding abstract methods found in interface Foo”,表明存在多个抽象方法。请注意,@FunctionalInterface不是必需的,但对于为此设计的接口而言,使用它是比较好的做法。它就像是@Override标注表示方法被重写了。

 

内置函数式接口

技术图片

 

 

 技术图片

 

 

 

三,Lambda快捷写法-方法引用

方法引用让可以重复使用现有的方法定义,并像Lambda一样传递它们。在一些情况下,比起使用Lambda表达式,它们似乎更易读,感觉也更自然。下面就是我们借助更新的Java 8API,用方法引用写的一个排序的例子:

先前: 

inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

之后(使用方法引用和java.util.Comparator.comparing):

 inventory.sort(comparing(Apple::getWeight)); 

方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。事实上,方法引用就是让根据已有的方法实现来创建Lambda表达式。但是,显式地指明方法的名称,的代码的可读性会更好。它是如何工作的呢?当需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面。

技术图片

 

 可以把方法引用看作针对仅仅涉及单一方法的Lambda的语法糖,因为表达同样的事情时要写的代码更少了。 如何构建方法引用 方法引用主要有三类。

(1)指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)。

(2)指向任意类型实例方法的方法引用(例如String的length方法,写作String::length)。

(3)指向现有对象的实例方法的方法引用(假设有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么就可以写expensiveTransaction::getValue)。

第二种和第三种方法引用可能乍看起来有点儿晕。类似于String::length的第二种方法引用的思想就是在引用一个对象的方法,而这个对象本身是Lambda的一个参数。例如,Lambda表达式(String s) -> s.toUppeCase()可以写作String::toUpperCase。但第三种方法引用指的是,在Lambda中调用一个已经存在的外部对象中的方法。例如,Lambda表达式()->expensiveTransaction.getValue()可以写作expensiveTransaction::getValue。

技术图片

 

以上是关于Java8 Lambda表达式的主要内容,如果未能解决你的问题,请参考以下文章

Java8——Lambda表达式

Java8新特性03 Lambda表达式

好程序员分享java8新特性之Lambda表达式

JAVA8之lambda表达式详解

Java8特性Lambda表达式

Java8新特性:Lambda表达式函数式接口以及方法引用