API Design with Java 8

2017 Mar 5

如果返回值可能为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更快更简洁地删除元素

2017 Jan 19

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

2016 Nov 23

这是一份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

2016 Nov 6

这是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对象可以通过下面的方法创建,

书籍推荐:Java SE8 for the Really Impatient

2016 Oct 13

推荐一本非常好的关于Java 8的书,Java SE 8 for the Really Impatient

亚马逊链接豆瓣链接

Java SE 8 for the Really Impatient