参考并建议阅读《Java8函数式编程》
转载请注明出处:http://blog.youkuaiyun.com/cuiods/article/details/53691534
一、类库
1、基本类型
Java的泛型是是基于对泛型参数类型的擦除,只有装箱类型才能作为泛型参数,然而,由于装箱类型是对象,所以在内存中占据更大的空间,比如int只要4个字节,而Integer确需要16个字节。
将基本类型转换为装箱类型称为装箱,反之成为拆箱,两者都需要额外的计算开销。为了减小这些性能开销, Stream 类的某些方法对基本类型和装箱类型做了区分。
对基本类型做特殊处理的方法在命名上有明确的规范。 如果方法返回类型为基本类型, 则在基本类型前加 To,如ToLongFunction。这些基本类型都有与之对应的 Stream, 以基本类型名为前缀, 如 LongStream。
public static void printTrackLengthStatistics(Album album) {
IntSummaryStatistics trackLengthStats
= album.getTracks()
.mapToInt(track -> track.getLength())
.summaryStatistics();
System.out.printf("Max: %d, Min: %d, Ave: %f, Sum: %d",
trackLengthStats.getMax(),
trackLengthStats.getMin(),
trackLengthStats.getAverage(),
trackLengthStats.getSum());
}
2、@FunctionalInterface
每个用作函数接口的接口都应该添加@FunctionalInterface注释。
并不是所有只有一个方法的接口都可以用lambda表达式来实现,有些接口不可以用作函数接口,例如java.lang.comparable接口,函数之间是不可比较的,因为在通常意义下我们不认为函数有属性或状态。
@FunctionalInterface会强制检查一个接口是否符合函数接口的标准,如果该注释添加给一个枚举类或另一个注释,或接口不止一个方法,javac都会报错。
3、默认方法
Java8中Collection接口中添加了stream()方法,在Java类库中实现Collection接口的类会需要添加该方法的实现。但是在用Java1-7编写的程序中,用户实现Collection接口的类则因为没有实现stream()而无法编译。为了不违反向下兼容的原则,java提供了这些接口的默认实现,成为默认方法。
Iterable中默认方法的示例:
default void forEach(Consumer<? super T> action) {
for (T t : this) {
action.accept(t);
}
}
4、多重继承
接口允许多重继承,在遇到两个接口包含签名相同的默认方法时,javac不明确应该继承哪个接口的中的方法,因此编译器会报错。但是如果在类中再次实现该方法则能解决该问题。
三定律:
- 类胜于接口。如果在继承链中有方法体或抽象的方法声明, 那么就可以忽略接口中定义的方法。
- 子类胜于父类。 如果一个接口继承了另一个接口, 且两个接口都定义了一个默认方法,那么子类中定义的方法胜出。
- 没有规则三。如果上面两条规则不适用, 子类要么需要实现该方法, 要么将该方法声明为抽象方法。
二、高级集合类和收集器
1、方法引用
lambda表达式有一个常见的用法,如
artist -> artist.getName()
Java8为其提供了一个简写语法,叫做方法引用,以上方法调用可以写为:
Artist::getName
标准语法为Classname::methodName。对于构造函数
(name, nationality) -> new Artist(name, nationality)
可以改写为
Artist::new
2、使用收集器
收集器是一种通用的、从流生成复杂值的结构。将收集器传给collect方法,则可以从流生成不同的数据结构。
除了前面介绍的toList,还有toSet和toCollection,分别生成set和collection的实例。Stream类库在背后自动选择合适的List和Set类型。
除了类库的提供的数据类型,还可以转换为自定义的集合类型:
stream.collect(toCollection(TreeSet::new));
收集器也可以让流生成一个值,比如minBy和maxBy允许按某种特定的顺序生成一个值。
public Optional<Artist> biggestGroup(Stream<Artist> artists) {
Function<Artist,Long> getCount = artist -> artist.getMembers().count();
return artists.collect(maxBy(comparing(getCount)));
}
除此之外还可以对数据分组:
public Map<Artist, List<Album>> albumsByArtist(Stream<Album> albums) {
return albums.collect(groupingBy(album -> album.getMainMusician()));
}
3、一些细节
关于Map类,一种通常的用法是使用Map类定义缓存,传统的处理方式是先试着从 Map 中取值, 如果没有取到, 创建一个新值并返回。
public Artist getArtist(String name) {
Artist artist = artistCache.get(name);
if (artist == null) {
artist = readArtistFromDB(name);
artistCache.put(name, artist);
}
return artist;
}
Java8为Map引入了新方法computeIfAbsent,使用该方法可以将上面的代码重写为
public Artist getArtist(String name) {
return artistCache.computeIfAbsent(name, this::readArtistFromDB);
}