【问题标题】:Extending Guava collections扩展 Guava 集合
【发布时间】:2015-06-02 08:58:40
【问题描述】:

我想扩展 LinkedHashMultimap (Guava 16.0.1),主要是为了添加返回常用预填充地图的方法。

public class MyMap extends LinkedHashMultimap<String, Object> {
}

但是,据我所知,大多数 Guava 集合都是最终的,而那些不是最终的,不会公开公共构造函数。这个设计决定的可能原因是什么?实现我的目标的最佳方法是什么?我能想到的最好的方法是在我的类中包装 Multimap 方法,但这远非理想。

编辑:

一些用户指出了不允许继承的正当理由。为了稍微扩展问题,我想指出几点。

  1. 如果公共API不推荐继承,Java自己的集合类怎么不是final的?

  2. 假设我广泛使用以下类:

LinkedHashMultimap,BiConsumer>

现在,这是一口。如果可以的话:

public class ConsumerMap extends LinkedHashMultimap<Class<? extends BaseEntity>, BiConsumer<? extends BaseEntity, ? extends Object>> {
}

然后我可以在我的代码中使用 ConsumerMap 来代替那个怪物。我相信仅可读性就足以证明继承是合理的。

【问题讨论】:

  • 装饰模式是你的方式吗?
  • 如果我理解正确,您将添加的方法无论如何都没有理由成为实例方法。为什么您需要扩展 LinkedHashMultimap 只是为了提供返回预填充地图的实用方法?组合通常比继承更可取,但在这里,您似乎甚至不需要组合:只需要一个实用程序类。你能提供一个你要添加的方法的例子吗?
  • @JBNizet:我意识到我可以通过实用程序类做到这一点,这样看起来更干净。顺便说一句,我不明白为什么组合比继承更受欢迎。两者都有合法的用例。
  • 而且这个用例不是一个合法的用例,既不是继承,也不是组合。关于“偏好组合优于继承”,请阅读books.google.fr/…
  • @JBNizet:在这种情况下,继承不会有危险,因为我只会使用极不可能更改的超类方法,但是,我现在了解阻止继承的原因。

标签: java guava


【解决方案1】:

机会是:

  1. 在 Java 8 中为接口添加默认方法(您不能这样做,因为集合类型是由 Guava 库定义的),
  2. 扩展实施(同样你不能这样做,因为实施大多是最终的)
  3. 为外部帮助类提供您想要的方法

【讨论】:

    【解决方案2】:

    这些类设计为不可扩展。因为继承本质上破坏了封装,所以当类无法扩展时,它使 API 更干净。 (相关话题:Good reasons to prohibit inheritance in Java?

    您通常可以通过使用合成来解决此问题。

    回答您的后续问题:

    关于 Java 集合 API,我认为有两种选择:要么像 ArrayListHashMap 这样的类被设计为扩展,要么这只是一个 API 设计缺陷。我怀疑后者;但在 API 发布后,不可能恢复这种设计选择。或许只有 AbstractList 这样的抽象类才应该被声明为非最终类?

    可能 Guava 团队认为像您展示的用例非常罕见,以至于不值得努力添加对继承 (YAGNI) 的支持。

    This talk 由 Josh Bloch 撰写——他参与了 Java API 和 Guava 的设计——是关于该主题的精彩演讲。

    【讨论】:

    • 这一切都很好,但我仍然倾向于不同意将课程定为最终是一件好事。它应该留给图书馆的用户来决定他们的用例是否证明继承是合理的。更多的选择几乎总是一件好事。而且 Guava 团队不必努力支持继承,实际上他们努力禁用它。
    • “更多的选择几乎总是一件好事。”绝对不。 Particularly when the options make it easy to shoot yourself in the foot.扩展集合类时很容易做错事。 “优先组合而不是继承”和“为继承而设计或阻止它”是这里的两个关键设计原则。 Guava 将像 LinkedHashMultimap 这样的普通实现与专门为继承设计的实现(Forwarding- 类)分开。
    【解决方案3】:

    正如其他人指出的那样,您应该更喜欢委托而不是继承。

    你想要实现你想要的类是扩展ForwardingSetMultimap,使用LinkedHashMultimap作为委托。这是一个外观示例:

    public final class MyMap extends ForwardingSetMultimap<String, Object> {
      private final SetMultimap<String, Object> delegate =
          LinkedHashMultimap.create();
    
      @Override public SetMultimap<String, Object> delegate() {
        return delegate;
      }
    
      // add your methods here
    }
    

    Aside:你问“如果在公共 API 中不推荐继承,为什么 Java 自己的集合类不是 final 的?”

    我认为答案基本上是不创建 Java 自己的集合类 final 是一个错误(注意:其中一些,特别是较新的,实际上是 final)。正如@Mick Mnemonic 所说,Josh Bloch(设计原始集合 API)参与了 Guava 集合 API 的设计:Guava API 中的设计决策反映了从原始集合 API 中吸取的经验教训。

    【讨论】:

    • 终于得到满意的答复。抱歉,我想我应该把 Guava 文档读到最后。 “新集合类型”部分没有提到转发装饰器,所以我不知道它们。
    猜你喜欢
    • 1970-01-01
    • 2015-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-12
    • 2016-07-08
    • 2015-09-19
    相关资源
    最近更新 更多