API Design with Java 8
如果返回值可能为null,那么使用Optional类型。 尽量不要把Optional做为接口参数,这会使得接口调用变得很笨拙。
public Optional<String> getComment() {
return Optional.ofNullable(comment);
}
不要使用数组做为返回值或者参数。
使用数组做为返回值时,为了防止调用者修改source数组,常常要先拷贝source数组再返回,效率不高(比如Enum::values()
)。
使用数组做为参数时,其它线程可能会同时在修改数组。
如果需要强调返回的集合是不可修改时,可以考虑使用Stream
做为返回类型。
public Stream<String> comments() {
return Stream.of(comments);
}
可以考虑增加静态的接口方法来创建接口的实现类。
相比Point point = new PointImpl(1,2);
而言,Point point = Point.of(1,2);
隐藏了具体的实现类。
考虑使用lambda,而不是继承,来个性化对象的行为。
//不要这样
Reader reader = new AbstractReader() {
@Override
public void handleError(IOException ioe) {
ioe. printStackTrace();
}
};
//better to expose a static method or a builder in the Reader interface that takes a Consumer<IOException> and applies it to an internal generic ReaderImpl
Reader reader = Reader.builder()
.withErrorHandler(IOException::printStackTrace)
.build();
给Functional Interface加上@FunctionalInterface
,保证接口只有一个抽象方法。
如果接口要求参数不为null
,考虑使用Objects.requireNonNull()
做参数验证,可以提前抛出异常。
不要过分考虑带来的性能损失,JVM会优化掉不必要的检查。
public void addToSegment(Segment segment, Point point) {
Objects.requireNonNull(segment); //可以更早地抛出异常
Objects.requireNonNull(point);
segment.add(point);
}
更多详见原文。
用removeIf和Lambda更快更简洁地删除元素
Java 8的Collection
接口提供了一个新的删除元素的方法,boolean removeIf(Predicate<? super E> filter)
。
它可以接受一个Lambda做为参数。
用这个方法可以写出更简洁的代码。
用一行代码,items.removeIf(i -> isDeletable(i));
,就可以替代下面的代码。
for (Iterator it = items.iterator(); it.hasNext();) {
if (isDeletable(it.next())) {
it.remove();
}
}
另外,对于ArrayList
而言,使用removeIf
比传统的iterator方式更快。
传统的iterator方式,每删除一个元素E都要把E之后的所有元素往前移动一位。
如果ArrayList
的大部分元素都要被删除,那么时间复杂度会是O(n^2)级别。
(实际上,Collection
接口提供的默认removeIf
实现就是用iterator方式来做删除的。)
ArrayList
类override了removeIf
方法,提供了更高效的实现。
新的实现先用一个BitSet
在一趟循环中标记哪些元素需要删除(O(n)复杂度),然后再在一趟循环中移动要保留的元素(O(n)复杂度)。
SAM type和Lambda
这是一份Java 8 Lambda的早期设计文档。 文档里Lambda的语法和最终的实现有一些细微的区别,但是不影响阅读。 最终的Lambda设计文档见这里。
Lambda也叫“闭包(closure)”。
在Java 8之前,anonymous inner class可以认为是一种闭包——它可以capture enclosing class里定义的变量,封装一段或者几段代码以便稍后执行。
anonymous inner classes的缺点是语法太啰嗦、this
不是lexically scoped、不能capture非final变量等。
Java 8引入Lambda是为了解决这些问题。
Java 8在引入Lambda时,并没有引入新的function type;而是借助SAM type来实现Lambda。 SAM(single abstract method) type是指只有一个抽象方法的接口。
Lambda expressions can only appear in context where it will be converted to a variable of SAM type.
Lambda表达式都会转化为一个SAM类型的变量。 凡是能接收SAM类型的地方,比如方法的参数,都可以接收一个Lambda表达式。
Comparator<String> c = (String s1, String s2) -> s1.compareToIgnoreCase(s2);
Java 8的Lambda表达式和anonymous inner class的一个不同之处是它们对this
的解读。
Lambda表达式中的this
指的是enclosing class中的this
。
anonymous inner class中的this
指的是inner class自己的this
。
Optional in Java 8
这是Java SE 8 for the Really Impatient的一篇笔记。
一个Optional对象要么有内容(contain a non-null value),要么其内容为空。 Optional如果使用不当并不能体现它的优势。比如,
// 下面两段代码都可能抛异常
Optional<T> optionalValue = ...;
optionalValue.get().someMethod();
T value = ...;
value.someMethod();
// 下面两段代码都有繁琐的if判断
if (optionalValue.isPresent())
optionalValue.get().someMethod();
if (value != null)
value.someMethod();
Optional定义了一些方法,这些方法可以在一个Optional对象有内容时才执行某段代码, 或者没有内容时提供替代性的值。 使用这些方法,才能让Optional发挥优势,使代码远离NullPointerException或者繁琐的null值判断。
// 如果Optional对象里有非null值,则执行某个操作;否则什么也不做
optionalValue.ifPresent(v -> results.add(v));
// 如果Optional对象里有非null值,那么把这个值赋给result;否者把"foo"赋给result
String result = optionalString.orElse("foo");
String result = optionalString.orElseGet(() -> SOME_MAP.get("key"));
String result = optionalString.orElseThrow(NoSuchElementException::new);
Optional对象可以通过下面的方法创建,