JDK8的随笔(04)_Lambda表达式扩展之Method References

Method References 方法参照

对于Method Reference了解之前先了解Lambda表达式。

参看:

JDK8的随笔(01)_Lambda表达式是个神马东东,初尝禁果

JDK8的随笔(02)_Lambda表达式进一步探讨

JDK8的随笔(03)_Lambda表达式的变量使用范围讨论

Method References 的出现场景

Lambda表达式本身是一个延迟加载采用实现函数式接口的方式来简短代码提高可读性(刚开始可能会降低可读性,熟了就觉得效率很高),减少各种缩进括号的繁杂表现形式。

但,有一些时候,我们的接口中的方法中也许调用的只是另一个方法而已(javadoc中写的是调用一个方法,当然调用复数个方法也是有可能的,后面会细说),除此以外并没有其他的处理,这个时候,如果采用JDK8的Method Reference来做的话可以到达更简便易读的效果。

还是举之前的Person的例子:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public Calendar getBirthday() {
        return birthday;
    }    

    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }
    }

定义一个Person的bean。

如果我们想针对一个Person的list进行按照年龄大小的排序,那么我们需要实现一个Comparator的class:

class PersonAgeComparator implements Comparator<Person> {
    public int compare(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}

这实现类PersonAgeComparator中实现了接口Comparator的compare的方法,而这个方法中也只是做了一个compareTo的调用。首先,这个Comparator的接口中有一个abstract的方法compare可以被实现,那么这个接口自然就是函数式接口,可以利用Lambda表达式来进行动态替换。

假设要调用排序的地方如下:

Arrays.sort(rosterAsArray, new PersonAgeComparator());

利用Lambda表达式后:

Arrays.sort(rosterAsArray,
    (Person a, Person b) -> {
        return a.getBirthday().compareTo(b.getBirthday());
    }
);

return和{ } 是可以省略的,那么就是如下的效果:

Arrays.sort(rosterAsArray,
    (Person a, Person b) ->  a.getBirthday().compareTo(b.getBirthday())
);

之前忘记说没说过了,领域模型的定义其实也是可以省略的,编译器会动态来进行核对,所以还可以继续省略:

Arrays.sort(rosterAsArray,
    (a, b) ->  a.getBirthday().compareTo(b.getBirthday())
);

变成了这样的效果。

按照Lambda表达式的写法来说这已经到达了最简单的写法了。

本来需要一个实现Comparator的class来处理的事情,我们使用一段Lambda已经简化了处理。

但是,可以试想这样的一个场景,如果我们有多个地方都要调用这个比较,而我们又不希望多加一个类来实现Comparator(这就是矫情。。。),那么就可以采用JDK8最新的Method Reference了。

从名称可以看出,这叫做方法参照。

也就是说,我们需要做的其实就是 a.getBirthday().compareTo(b.getBirthday()这么一个简单的操作,如果这个简单的操作在某个方法中已经被实现了,是否可以直接参考那个方法即可呢?答案是YES。

因为我们在Person的class中已经定了一个处理相同的compareByAgede方法,那么这里的Lambda的处理部分就可以采用这个已定义的方法来替代,效果变成:

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

最终的简化的写法:

Arrays.sort(rosterAsArray, Person::compareByAge);

惊人的一个结果。的确是。

写到这里,可能有一个很大很大的疑问就来了。

众所周知,Arrays.sort()方法的第二个参数应该是一个实现了Comparator的实现类,这样才体现出接口的意义。而现在我们不仅没有用类,也没有用那个实现类的方法,那么这个接口的限制岂不是没有了?java的模式岂不是就乱了?

首先,没有用类来实现的原因是引入了Lambda表达式,采用表达式的动态后期迟延绑定的方式本身是新的特性,本身这个特性就默认了一个前提,那就这个动态的实现是在实现函数式接口中那唯一的一个abstract方法,所以不用管接口中的方法叫啥如何实现,我们只考虑这个Lambda指定就实现这个方法。从这个角度来说,已经绑定了相应的方法也就算是默认实现了这个接口的特定方法了。

其次,在实现方法的同时,如果发现了一个一模一样的其他地方的方法实现,那么可以利用最新的Method Reference来动态的平移调用,采用其他地方的方法调用来实现Lambda表达式本来要实现的逻辑。

根据上面亮点来讲,首先默认了是想方法是什么,保证了接口的纯洁性和无二义性,其次采用方法一致性平移的方式保证代替方法和原方法的一致来实现最终的Method Reference。

根据上面的总结还可以看到很重要的两点,也是javadoc中明确写清的:

1. 上面例子中的Comparator<Person>.compare的参数是(Person, Person),那么我们能替代的方法必须和这个参数保持100%的一致,因为方法名字已经没有限制了。

2. 返回值类型必须100%的一致。

保证了上面的1和2的两点后,我们才可以进行方法的参照,才可以随心所欲地进行Reference。

Method References 的四种类型

下一节继续写。

つづく???

时间: 2024-10-07 06:33:00

JDK8的随笔(04)_Lambda表达式扩展之Method References的相关文章

JDK8的随笔(01)_Lambda表达式是个神马东东,初尝禁果

Lambda表达式 先写写背景和最基本的东东,泛型加入和各种循环的复杂模式后面再慢慢深入. 需要看JDK8的背景 虽然网上的介绍很多,但是不如自己读一下document后来得正宗. 说一下缘由,突发的这个项目客户貌似是个暴发户,发疯什么都要用最新的,JDK8是否稳定也不管,各种要求最新.Lambda语法全上,各种jdk8最新的东西全往上搞,我靠...WS还有其他的容器是否对8的支持很好也不知道....不过,这也不是坏事,学呗~~~~ 等jdk出到12的时候再回头看看也挺有意思. 本文也是以JDK

JDK8的随笔(03)_Lambda表达式的变量使用范围讨论

Lambda变量使用以及使用范围 概念普及 捕获变量 capture variables 啥是capture variables 先看一段代码的例子: public class LocalClassExample { static String regularExpression = "[^0-9]"; public static void validatePhoneNumber( String phoneNumber1, String phoneNumber2) { final in

基础很重要~~04.表表达式-上篇

以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL基础]07.数据修改 [T-SQL基础]08.事务和并发 [

基础很重要~~04.表表达式

阅读目录 概述: 一.视图 二.内联表值函数 三.APPLY运算符 以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T

基础很重要~~04.表表达式-下篇

以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL基础]07.数据修改 [T-SQL基础]08.事务和并发 [

ES6随笔--各数据类型的扩展(3)--函数

ES6随笔--各数据类型的扩展(3)--函数 1. 函数参数的默认值 可以在函数参数中指定默认值,函数内不能再用let或const声明: 使用参数默认值时,不能出现同名参数: 参数默认值惰性求值,每次调用函数会重新计算参数默认值表达式: let x = 99; function foo(p = x + 1) { console.log(p); } foo() // 100 x = 100; foo() // 101 可以与解构赋值默认值结合使用: function ({x, y = 5} = {

ES6随笔--各数据类型的扩展(1) --字符串和正则

ES6随笔--基本数据类型的扩展 字符串 Unicode表示法 \uxxxx表示的Unicode如果码数超过了0xFFFF的范围,只要放进大括号内就能识别: codePointAt() 用codePointAt()可以准确识别码数超出0xFFFF范围的Unicode字符: for...of循环能够正确识别32位的UTF-16字符: String.fromCodePoint() 与codePointAt()作用刚好相反:根据传入的Unicede码数返回对应的字符: 遍历 for...of循环可以循

JDK8的随笔(05)_Method References的种类继续啰嗦一下

Method References的种类 我觉得国内的盗链以及随意的盗文的确很严重... 有必要以后每次都写一下文章来源:blog.csdn.net/forevervip 这几天本想把Method Reference赶紧收尾然后写下一个Aggregate的说明,因为Aggregate是Lambda和Method Reference的混合载体,但是一直被一个问题困扰解释不明白,连泛型后来都重新看了一遍,最后想起JVM的一些底层理论才解决.后面写到Aggregate的时候再来讨论. Method R

JavaScript权威指南第04章 表达式和运算符

www.qdmm.com/BookReader/1845423,31051137.aspx www.qdmm.com/BookReader/1845423,31073665.aspx www.qdmm.com/BookReader/1845423,31088923.aspx www.qdmm.com/BookReader/1845423,31104070.aspx www.qdmm.com/BookReader/1845423,31114804.aspx www.qdmm.com/BookRea