• 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。

  • 相关阅读:
    1924班网址
    20192414《网络空间安全导论》第一周学习总结
    H-Angry
    A-Plague Inc
    B-Rolling The Polygon
    F-Moving On
    A-Maximum Element In A Stack
    J-Word Search
    E-Pawns
    D-Lift Problems
  • 原文地址:https://www.cnblogs.com/ooo0/p/11438415.html
Copyright © 2020-2023  润新知