8

客观的

创建一个类以用作String对象的不可变列表。

方法

我决定利用Google GuavaImmutableList<E>集合,而不是用Collections.unmodifiableList(List<? extends T> list)包装一个简单的List<E>因为我知道这可以避免对支持List<E进行不必要的并发检查>,它不知道被包装(来源:ImmutableCollectionsExplained)。

要求

  • 具有跨线程使用的“价值持有者”的类
  • 不应允许任何代码在创建后更改内部值

锦上添花

尝试

这里有一些尝试,虽然更多的组合是可能的。原谅幽默的演绎。

尝试#1(包括使用示例)

import java.util.List;
import com.google.common.collect.ImmutableList;
class BritnetSpearsSpellings implements Iterable<String> {
  public static BritnetSpearsSpellings of(String... spellings) {
    BritnetSpearsSpellings britneySpears = new BritnetSpearsSpellings();
    britneySpears.spellings = ImmutableList.copyOf(spellings);
    return britneySpears;
  }
  private List<String> spellings;
  private BritnetSpearsSpellings() {
  }
  public List<String> getSpellings() {
    return spellings;
  }
}
@Override
public Iterator<String> iterator() {
  return spellings.iterator();
}
public class Usage {
  public static void main(String[] args) {
    for (String sepllin : BritnetSpearsSpellings.of("Brittany Spears", "Brittney Spears", "Britany Spears"))
      System.out.printf("You spel Britni like so: %s%n", sepllin);
    }
  }
}

尝试#2

class BritnetSpearsSpellings implements Iterable<String> {
  public static BritnetSpearsSpellings of(String... spellings) {
    BritnetSpearsSpellings britneySpears = new BritnetSpearsSpellings();
    britneySpears.spellings = ImmutableList.copyOf(spellings);
    return britneySpears;
  }
  private ImmutableList<String> spellings;
  private BritnetSpearsSpellings() {
  }
  public ImmutableList<String> getSpellings() {
    return spellings;
  }
  @Override
  public Iterator<String> iterator() {
    return spellings.iterator();
  }
}

尝试#3

class BritnetSpearsSpellings implements Iterable<String> {
  public static BritnetSpearsSpellings of(String... spellings) {
    BritnetSpearsSpellings britneySpears = new BritnetSpearsSpellings(ImmutableList.copyOf(spellings));
    return britneySpears;
  }
  private final ImmutableList<String> spellings;
  private BritnetSpearsSpellings(ImmutableList<String> spellings) {
    this.spellings = spellings;
  }
  public ImmutableList<String> getSpellings() {
    return spellings;
  }
  @Override
  public Iterator<String> iterator() {
    return spellings.iterator();
  }
}

差异总结

  • 1 将List<E>保留在公共接口中,记录 JavaDoc 中的不变性。

  • 2 将所有内容存储并公开为Google GuavaImmutableList<E>

  • 3 以创建专用构造函数为代价将内部引用保持为最终引用,这可能会使静态工厂方法初始化在没有其他初始化选项的情况下看起来很傻(实际上在真实类中存在)

问题

请帮助我在其中一种实现中进行选择,并说明您的选择背后的原因。

我认为方法 2 的主要缺点是客户需要了解/了解专门的Google Guava类型,而他们可能不应该这样做?

4

3 回答 3

3

显然,在我看来,#3 是最好的选择。强制并记录类(该字段)的不可变性,而不仅仅是其本身不可变final的事实。List

List无论您是使用一些 javadoc 还是ImmutableList在 getter 中公开一个实际值,都是另一回事。我已经看到一些意见认为返回ImmutableList文档的意图很明确,但是,您仍然将自己绑定到实现而不是接口,以实现低级别并且将来可能需要更改的东西(即使不变性是“通常”好的)。因此,对于您的应用程序而言,它更像是一种全球设计选择,而不仅仅是一个用例。如果您确实使用ImmutableList,我认为这对客户来说不是一个真正的问题,名称是明确的,并且可以通过您的 IDE 轻松看出它是 的实现List,如果他们想要更多信息,他们可以访问 javadoc。谁知道呢,他们可能会喜欢它并开始使用它,

于 2012-09-18T09:52:33.570 回答
1

从您的问题陈述(“给定的一组字符串应该只有一个类”),听起来您真正想要的是一个Interner<ImmutableSet<String>>.

  • Interner是一种实用程序(用 构造一个Interners.newStrongInterner()),用于确保您只有一个具有任何给定数据的对象实例。
  • 如果您的集合是不可变的并且支持成员资格测试的任意顺序的集合,则ImmutableSet是正确的选择。

或者,您可能想查看一个Cache<ImmutableSet<String>>(有关详细信息,请参阅Caches Explained)。

于 2012-09-18T21:34:20.923 回答
0

我会使用Attempt #2over Attempt #1,因为它记录了您总是返回 ImmutableList 实例。一般来说,选择尽可能具体的返回类型和尽可能通用的参数类型是有用的。但是对于这两种情况,都更喜欢接口而不是具体的类。

嗯,ImmutableList是一个抽象类而不是一个接口。但是由于它的性质,它的行为非常像一个界面。

Attempt #3spellings当您的类成员引用在对象的生命周期内无法更改和/或您希望使整个类本身不可变时,这是有意义的。否则,spellings在对象生命周期内何时可以将其分配给另一个 ImmutableList 则没有意义。

鉴于您示例中的用例,我倾向于说#3 是最佳选择。

于 2012-09-18T09:50:27.213 回答