zoukankan      html  css  js  c++  java
  • 读书笔记,《Java 8实战》,第三章,Lambda表达式

    第一节,Lambda管中窥豹
       可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式,它没有名称,但它有参数列表、函数主题和返回值。
       本节介绍了Lambda表达式的语法,它包括参数列表、箭头、Lambda的主体,其中Lambda主体可以包括多行,这个时候必须用大括号把它括起来。
       本节的最后作者给出了Lambda语法的几个例子,让我们来辨别什么是合法的Lambda表达式语法,其中在没有大括号的时候用return是不对的,带有大括号的时候用单行的表达式也是不对的。
     
    第二节,在哪里以及如何使用Lambda
       我们可以在函数式接口上使用Lambda表达式,那么本节中作者也提出了什么是函数式接口?函数式接口就是只有一个抽象方法的接口,比如传统Java里面的Comparator, Runnable, EventListener,Callable, PrivilegedAction。
       然后这里作者又提出了默认方法的概念。所谓的默认方法就是在类没有对接口方法进行实现的时候,在接口主体也为方法提供了默认实现的方法,哪怕有很多默然方法,只要接口只有一个抽象方法,那它就仍然是一个函数式接口。
       作者在这里给了几个让我们辨认什么是函数式接口的例子,有两个错误的例子,一个是里面一个借口都没有定义的,另外一个里面虽然自定义一个接口,但是他还从父接口中继承了一个抽象接口。
       函数式接口的抽象方法的签名,基本上就是Lambda表达式的签名,我们将这种抽象方法叫做函数描述符。比如Runnnable接口可以看做一个什么也不接受,什么也不返回的函数的签名。
       关于可以在哪里使用Lambda,其实有很多,比如可以作为函数的入参、作为函数的返回值、又或者把它赋值给一个局部变量。后作者提到了一个叫做@FunctionalInterface的注解
     
    第三节,把Lambda付诸实践,环绕执行模式。
       所谓的环绕执行模式其实就类似于以前我们学到的模版方法模式,这里使用的是try...catch的例子。当然在模板方法中,我们一般是通过在子类重写抽象方法的形式来让模板方法具有不同的行为,在本节中,我们可以通过表达式的方式直接把代码传递给方法,从而实现不同的行为。
     
    第四节,使用函数式接口
       函数式接口很有用,因为抽象方法的签名可以描述Lambda表达式的签名,函数式接口的抽象方法的签名称为函数描述符,所以为了运用不同的表达式,你需要一套能够描述常见描述函数描述符的函数式借口。在Java的API中已经有了几个函数式的接口。比如Comparable, Runnable ,Callable, 在Java8中,设计师在java.util.funtion包又添加了几个新的函数是接口,比如Predicate ,Consumer ,Funtion。
       然后作者又提到了这种函数式接口对于基本类型的特化,为什么要特化呢,因为对于原始类型的装箱和拆箱性能比较低。有了特化之后就可以避免这种装箱拆箱的操作。
       然后再在p46页中给出了一个表格,其中列出了在Java8中的常用的函数式接口。主要就是断言、消费者、函数,以及它们的单参数、多参数、基本类型特化的各种变体。
       在这里作者提到了任意函数式接口都不允许抛出受检异常,如果你需要Lambda表达式来抛出异常,有两种方法:第一种是定义一个自己的函数是接口,并申明受检异常;第二,把Lambda包在一个try catch块中。
     
    第五节,类型检查,类型推断以及限制
       Lambda的类型是从使用Lambda的上下文中推断出来的。上下文中Lambda表达式需要的类型称为目标类型有了目标类型的概念,那么同一个Lambda表达式就可以与不同的函数式接口联系起来,只要他们的抽象方法签名能够兼容就行。大家可能还记得在Java7中就已经引入的菱形运算符,利用泛型推断从上下文中推断类型的思想。
       这里还有一个特殊的void 的兼容规则, 也就是说,如果一个Lambda的主体是一个语句表达式,他就和一个返回void 的函数描述符兼容。
       第四小节,使用局部变量
       我们迄今为止所介绍的所有Lambda表达式都只用到了其主体里面的参数,但是人们还是也允许使用自由变量,就像匿名类一样。所谓自由变量,就是在外层作用域中定义的变量虽然它可以使用自由变量,但其实是有一些限制的。Lambda可以没有限制的使用实例变量和静态变量,但是局部变量必须显示的声明为final。换句话说就是lambda表达式,只能捕获指派给他们的局部变量一次之所以会有这种限制,是因为局部变量是分配在栈上的,而实例变量和静态变量是分配在堆栈的。基于java中对于自由变量的这种限制,所以它实际上不具有JS中的那种闭包的概念。
     
    第六节,方法引用
       方法应用可以被看作仅仅调用特定方法的Lambda的一种快捷写法,它的基本思想是,如果Lambda代表的只是直接调用这个方法,那最好还是用名称来调用它,而不是去描述如何调用它。因为显示的指定方法的名称,你的代码的可读性会更好。
     
       在这里,作者给出了,三种类型的方法引用:
       第一种,指向静态方法的方法应用,比如integer的parseInt方法。
       第二种,只想问你类型实例方法的方法已有,比如String的 length方法。
       第三种,指向现有对象的实例方法的方法应用,这个所谓的现有对象,不是表达式的入参,而是上文提到的那种自由变量,用就是外层作用域中的变量。
       第二小节,构造函数引用
       也就是说对于一个现有的构造函数,你可以利用它的名称和关键字new,来创建它的一个引用,像这样Classname::new 但是这个方法应用创建好之后赋值给谁就有点讲究了,这主要是根据这个构造函数的参数的多少而有所不同,比如说没有参数、一个参数、两个参数等等,分别可以给Supplier, Function ,BiFunction,。
       构造函数应用的一个典型应用就是用来构造简单工厂方法,直接把每一个类的构造函数注册到工厂的字典里面去。
     
    第七节,lambda和方法应用实战
       这一节唯一要注意的就是Comparator的那个看comparing 静态辅助方法。他可以接受一个function来提取comparable键值 ,并生成一个Comparator 对象,他的实现代码如下:
     public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
            Objects.requireNonNull(keyExtractor);
            return (Comparator<T> & Serializable)
                (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
    }
     
    第八节,复合Lambda 表达式的有用方法
       这一节主要列出了Java8新提供的几个函数式接口:Comparator, Function, Predicate新提供的默认方法来作为复合方法。有了复合方法之后,就意味着你可以把多个简单的Lambda复合成复杂的表达式。比如你可以让两个谓词做一个or操作,组合成一个更大的谓词,而且你还可以让一个函数的结果成为另一个函数的输入,你可能会想函数式接口怎么可能有更多的方法呢?窍门在于我们即将介绍的方法都是默认方法,也就是说它们不是抽象方法。然后作者详细地给出了,比较器复合,谓词复合 、函数复合。具体都包含哪些复合函数就要自己去看了。这些复合函数组合起来还确实是挺神奇的。
     
  • 相关阅读:
    一个程序员的职业规划
    基于Andoird 4.2.2的Account Manager源代码分析学习:创建选定类型的系统帐号
    [置顶] C++学习书单
    js快速分享代码
    The declared package does not match the expected package
    IBM Rational Appscan Part 1
    IBM Rational Appscan: Part 2 ---reference
    阅读redis源代码的一些体会
    18 Command Line Tools to Monitor Linux Performance
    代码规范
  • 原文地址:https://www.cnblogs.com/strinkbug/p/6391921.html
Copyright © 2011-2022 走看看