【问题标题】:ImmutableList vs List- what should I cast it as?ImmutableList vs List-我应该把它转换成什么?
【发布时间】:2015-04-15 15:22:25
【问题描述】:

我知道将所有 List 实现转换为 List 是普遍接受的。无论是变量、方法返回,还是使用ArrayListCopyOnWriteArrayList等的方法参数。

List<Market> mkts = new ArrayList<>();

当我使用 Guava ImmutableList 时,我感觉它可以说是这条规则的一个例外(特别是如果我正在构建内部的复杂业务应用程序而不是公共 API)。因为如果我将其降级为列表,已弃用的 mutator 方法将不再被标记为已弃用。此外,它不再被识别为不可变对象,这是其功能和身份的重要组成部分。

List<Market> mkts = ImmutableList.of(mkt1,mkt2,mkt3);

因此,将其作为ImmutableList 传递是有意义的,对吧?我什至可以争辩说,内部 API 只接受 ImmutableList 是一个很好的策略,因此客户端的可变性和多线程不会破坏库中的任何内容。

ImmutableList<Market> mkts = ImmutableList.of(mkt1,mkt2,mkt3);

我知道ImmutableList 本身存在被弃用的风险,而甲骨文决定创建自己的ImmutableList 的那一天将需要大量重构。但是,维持ImmutableList 演员阵容的利大于弊是否值得商榷?

【问题讨论】:

  • 在这种情况下,我将直接使用ImmutableList&lt;Whatever&gt;。如果 Oracle 实现了这样的类,正在进行的重构不应该让你担心,否则我们都害怕将来发生变化,并且不会生成代码。
  • 说得很好,路易吉。而当我实现ImmutableList 时,我很少遇到需要稍后将其切换为可变列表实现的情况。当我制作不可变的东西时,应用程序的整个结构都是围绕它不可变这一事实构建和优化的。
  • 另一种选择是使用List&lt;? extends Market&gt; 作为列表的类型。它会给你一点编译器强制的不变性——例如,你不能调用mkts.add(new Market())。但这并没有像ImmutableList 那样声明意图

标签: java collections guava


【解决方案1】:

我同意你的理由。如果您使用的是 Guava 集合库并且您的列表是不可变的,那么将它们传递为 ImmutableList 是个好主意。

但是:

我知道 ImmutableList 本身存在被弃用的风险,而 Oracle 决定创建自己的 ImmutableList 的那一天将需要大量重构。

第一种情况似乎不太可能,但每当您使用 any 3rd-party 库时,您都需要承担风险。但另一方面,如果他们 (Google) 无故弃用您依赖的类或方法,您可以选择不升级应用程序的 Guava 版本。

更新

Louis Wasserman(为 Google 工作)在评论中说:

“Guava 为非@Beta API 提供了非常强大的兼容性保证。”

因此,我们可以排除无偿 API 更改的可能性。


第二种情况更不可能(IMO)。并且您可以确定,如果 Oracle 确实添加了不可变列表类或接口,则不会要求您进行重构。 Oracle 在增强标准 API 时非常努力地避免破坏现有代码。

但是话虽如此,这真的取决于你权衡利弊......以及如果最终发生你将如何处理利弊。

【讨论】:

  • Oracle 和 Guava 也互相讨论这些事情,供参考,Guava 为非@Beta API 提供了相当强的兼容性保证。
  • 还有一个好处; Immutable* 类覆盖来自核心集合接口(add、put 等)的所有可变方法,以将它们标记为已弃用。因此,如果您对变量使用 Immutable* 类型,您的 IDE 会在您尝试调用 list.add(something) 时发出警告。
【解决方案2】:

不幸的是,Java 中没有相应的接口(而且很可能永远不会有)。所以我的看法是假装ImmutableList 是一个接口。 :D 但说真的,它添加了不应该丢失的重要信息。

这一切都源于古老的规则,实际上是“针对接口的程序”之类的东西。 IIRC 在创建规则时,周围没有 Java,“接口”是指编程接口,即合约,而不是 java interface

类似的方法

void strange(ArrayList list) {...}

显然很奇怪,因为没有理由不使用List。包含ImmutableList 的签名是有充分理由的。


我知道 ImmutableList 本身存在被弃用的风险,而 Oracle 决定创建自己的 ImmutableList 的那一天将需要大量重构。

你是说 Java 18?让我们看看,但是 Guava 的 ImmutableList 相当不错,并且以不同的方式设计这样的类没有多大意义。因此,您可以希望大多数更改仅发生在您的导入中。到 2050 年,会出现比这更严重的问题。

【讨论】:

    【解决方案3】:

    继续使用List而不是ImmutableList这没有问题,您的API也没有理由明确地开始使用ImmutableLists,原因如下:

    1. ImmutableList 只是 Guava,在任何时候都不太可能成为标准 Java。不要将您的代码和编码习惯与第三方库绑定(即使它是像 Guava 这样很酷的库)。
    2. 在 Java 中使用不可变对象是一种很好的做法,并且在开发 API 时尤其重要(请参阅有效的 Java 第 15 条 - 最小化可变性)。这是一个可以认为是理所当然的一般概念,不需要以接口的名义传达。同样,您不会考虑调用为继承而设计的 User 类UserThatCanBeSubclassed
    3. 以稳定性的名义,您的 API 绝不应该开始修改传递给它的 List,并且在将 List 传递给客户端时始终制作防御性副本。在此处引入ImmutableList 会诱使您和您的 API 的客户产生虚假的安全感,并诱使他们违反该规则。

    【讨论】:

      【解决方案4】:

      我理解你的困境。

      就个人而言,我建议继续使用List 作为引用类型(以防未来并从多态中受益),并使用@Immutable 注释来传达它是不可变的信息。

      注释比普通的 javadoc cmets 更明显,您甚至可以使用来自JSR-305(ex-JCIP)的注释。 一些静态分析工具甚至可以检测到它并验证您的对象没有发生变异。

      【讨论】:

        【解决方案5】:

        我宁愿只使用List 作为方法参数。强制调用者传递 ImmutableList 并没有多大好处 - 这是您自己的方法,无论如何您都不会改变列表,但您将拥有更可重用和通用的方法。

        作为返回类型,我会使用ImmutableList 让方法用户知道这个列表不能被修改。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-12-17
          • 1970-01-01
          • 2011-10-17
          • 1970-01-01
          相关资源
          最近更新 更多