Method References 方法参照
对于Method Reference了解之前先了解Lambda表达式。
参看:
JDK8的随笔(01)_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 的四种类型
下一节继续写。
つづく???