美文网首页
ITEM 21: 为子代设计接口

ITEM 21: 为子代设计接口

作者: rabbittttt | 来源:发表于2019-05-30 22:18 被阅读0次

ITEM 21: DESIGN INTERFACES FOR POSTERITY
  Java 8 之前,在不破坏现有实现的情况下向接口添加方法是不可能的。如果向接口添加新方法,现有的实现通常会缺少该方法,从而导致编译时错误。在 Java 8 中,添加了默认方法构造,目的是允许向现有接口添加方法。但是,向现有接口添加新方法充满了风险。默认方法的声明包含一个默认实现,所有实现接口但不实现默认方法的类都使用该默认实现。虽然向Java添加默认方法使向现有接口添加方法成为可能,但不能保证这些方法在所有现有实现中都能工作。默认方法被“注入”到现有的实现中,而不需要实现者的知识或同意。在 Java 8 之前,编写这些实现时默认它们的接口永远不会获得任何新方法。
  在Java 8的核心集合接口中添加了许多新的默认方法,主要是为了方便使用lambdas (第6章)。但是,不可能总是编写一个默认方法来维护每个可能实现的所有不变量。
  例如,考虑 removeIf 方法,它被添加到 Java 8 中的 Collection 接口。此方法删除给定布尔函数(或谓词)返回true的所有元素。默认实现指定为使用集合的迭代器遍历集合,对每个元素调用谓词,并使用迭代器的remove方法删除谓词返回true的元素。宣言大概是这样的:

// Default method added to the Collection interface in Java 8
default boolean removeIf(Predicate<? super E> filter) {
  Objects.requireNonNull(filter);
  boolean result = false;
  for (Iterator<E> it = iterator(); it.hasNext(); ) {
    if (filter.test(it.next())) { 
      it.remove();
      result = true;
    } 
  }
  return result; 
}

  这是为 removeIf 方法编写的最好的通用实现,但遗憾的是,它在一些实际的集合实现中失败了。例如,考虑 org.apache.commons.collections4.collection.SynchronizedCollection。这个类来自Apache Commons 库,类似于 java.util 包中的 Collections.-synchronizedCollection。Apache 版本还提供了使用客户机提供的对象来代替集合锁定的功能。换句话说,它是一个包装器类(Item 18),它的所有方法在委托给包装好的集合之前都会同步到一个锁定对象上。
  Apache SynchronizedCollection 类仍然在积极地维护,但是在撰写本文时,它还没有覆盖 removeIf 方法。如果这个类与 Java 8 一起使用,那么它将继承 removeIf 的默认实现,而 removeIf 并没有(实际上也不能)维护类的基本承诺:自动同步每个方法调用。默认实现对同步一无所知,并且无法访问包含锁定对象的字段。如果客户端调用 SynchronizedCollection 实例上的 removeIf 方法,同时存在其他线程对该集合的并发修改,则可能会导致 ConcurrentModificationException 或其他未指定的行为。
  为了防止在类似的 Java 平台库实现中发生这种情况(通过 Collections.synchronizedCollection 返回的包私有类)。为了在调用缺省实现之前执行必要的同步,JDK 维护人员必须覆盖缺省的 removeIf 实现和其他类似的方法。不属于 Java 平台一部分的现有集合实现没有机会与接口更改同步进行类似的更改,有些还没有这样做。
  在存在默认方法的情况下,接口的现有实现可能在编译时没有错误或警告,但在运行时失败。虽然这个问题并不常见,但也不是孤立的事件。已知 Java 8 中添加到集合接口的一些方法是易受影响的,已知已有的一些实现是受影响的。
  应该避免使用默认方法向现有接口添加新方法,除非有必要,在这种情况下,您应该仔细考虑现有接口实现是否会被默认方法实现破坏。但是,默认方法对于在创建接口时提供标准方法实现非常有用,可以简化实现接口的任务(item 20)。同样值得注意的是,默认方法的设计目的不是支持从接口中删除方法或更改现有方法的签名。如果不破坏现有客户机,这两种接口更改都是不可能的。
  这个故事的寓意很明显。尽管默认方法现在是 Java 平台的一部分,但是非常小心地设计接口仍然是最重要的。虽然默认方法使向现有接口添加方法成为可能,但是这样做风险很大。如果一个界面有一个小缺陷,它可能会永远激怒它的用户;如果一个接口有严重的缺陷,它可能会毁灭包含它的API。
  因此,在发布之前测试每个新接口是非常重要的。多个程序员应该以不同的方式实现每个接口。至少,您应该以三种不同的实现为目标。同样重要的是编写多个客户机程序,这些程序使用每个新接口的实例来执行各种任务。这将大大有助于确保每个接口满足其所有预期用途。这些步骤将允许您在接口发布之前发现它们的缺陷,而此时您仍然可以轻松地纠正它们。虽然在接口发布后可能会纠正一些接口缺陷,但您不能指望它。

相关文章

网友评论

      本文标题:ITEM 21: 为子代设计接口

      本文链接:https://www.haomeiwen.com/subject/ekwbtctx.html