Java 8的API设计原则

  掌握的Java API 8设计,才能确保客户端代码可以使用lambda表达式访问API。 一个好的API的设计需要仔细思考和大量的经验。因为一旦一个API公布,从一开始就贯彻坚定的承诺是非常重要。公共API,就像钻石,永远留传。 你只有一次得到它的机会,所以给她最好的。

API结合平衡了两个世界:坚定的和精确的承诺;以及具有高度的灵活性。下面是清单:

如果一个值缺了不要返回Null

不一致的空值处理(导致无处不在的NullPointerException异常)是Java应用程序历史上最大错误的唯一来源。 一些开发者认为引进null的概念是在计算机科学领域有史以来最严重的错误之一。 幸运的是,解决Java空值处理问题的第一步是Java 8中引入的Optional。 确保可以返回一个可选的对象,而不是null。

这样做:

public Optional<String> getComment() { return Optional.ofNullable(comment); } 

不要这样做:

 public String getComment() { return comment; // comment is nullable } 

 

不要用数组将值在API之间传递

Java5引入枚举Enum类型,其方法values()是返回所有枚举元素值数组,每次进行values()调用会在内部复制一份数组,这导致差性能和客户端代码差可用性。

如果返回的是一个不可修改的List就好得多,比如Stream,如果API返回元素集合,最好返回的是只读类型。

可以这样做:

public Stream<String> comments() {
    return Stream.of(comments);
}

不要这样做:

public String[] comments() {
    return comments; // Exposes the backing array!
}

 

提供静态接口方法实现对象创建的单一入口

避免客户端代码直接选择接口实现类,这会造成紧耦合。

这样做:

Point point = Point.of(1,2);

不要这样做:

Point point = new PointImpl(1,2);

 

使用函数接口和Lambda而不是继承实现组合

为了避免继承API造成客户端代码与API紧耦合,考虑使用静态接口方法实现组合。

这样做:

Reader reader = Reader.builder()
    .withErrorHandler(IOException::printStackTrace)
    .build();

不要这样做:

Reader reader = new AbstractReader() {
    @Override
    public void handleError(IOException ioe) {
        ioe. printStackTrace();
    }
};

 

确保增加@FunctionalInterface元注解到函数接口中

使用@FunctionalInterface可以让API用户使用Lambda实现你的函数接口。

这样做:

@FunctionalInterface
public interface CircleSegmentConstructor {
    CircleSegment apply(Point cntr, Point p, double ang);
    // abstract methods cannot be added
}

不要这样做:

public interface CircleSegmentConstructor {
    CircleSegment apply(Point cntr, Point p, double ang);
    // abstract methods may be accidently added later
}

 

避免使用函数接口作为参数过载方法

如果有多个函数有相同名称,都是将函数化接口作为参数,那么会造成客户端创建lambda混乱。

不要这样做:

public interface Point {
    add(Function<Point, String> renderer);
    add(Predicate<Point> logCondition);
}

这样做:

public interface Point {
    addRenderer(Function<Point, String> renderer);
    addLogCondition(Predicate<Point> logCondition);
}

后者客户端可以使用 point.add(p -> p + “ lambda”) 调用。

 

避免过度使用接口中默认方法

默认方法有应该是短和“根本”的功能,不要加入太多内容。

不要这样做:

public interface Line {
    Point start();
    Point end();
    default int length() {
        int deltaX = start().x() - end().x();
        int deltaY = start().y() - end().y();
 
    return (int) Math.sqrt(
        deltaX * deltaX + deltaY * deltaY
        );
    }
}

这样做:

public interface Line {
    Point start();
    Point end();
    int length();
}

 

确保API方法在被执行前检查参数不变性

不要这样直接调用:

public void addToSegment(Segment segment, Point point) {
    segment.add(point);
}

增加必要的检查:

public void addToSegment(Segment segment, Point point) {
    Objects.requireNonNull(segment);
    Objects.requireNonNull(point);
    segment.add(point);
}

 

不要简单调用Optional.get()

 Optional.get()其实是Optional.getOrThrow(),在get之前检查一下,这样不会抛出exception

这样做:

Optional<String> comment = 
// 某个 Optional值
 String guiText = comment .map(c -> "Comment: " + c) .orElse("");

 

Java专题