如何设计一个包含java.lang.String的不可变值类?

时间:2012-09-18 09:12:20

标签: java guava immutability

目的

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

方法

我决定利用 Google Guava ImmutableList<E> 集合,而不是包装一个简单的 List<E> 使用 Collections.unmodifiableList(List<? extends T> list) 因为我理解这可以避免对后备 List<E> 进行不必要的并发检查,后者不知道被包装(来源:{{ 3}})。

要求

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

尼斯到富人

尝试

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

尝试#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在公共接口中保留 String ,记录JavaDoc中的不变性。

  • List<E> Google Guava

  • 存储并公开所有内容
  • 3将内部引用保留为final,代价是创建一个专门的构造函数,可能会使静态工厂方法初始化看起来很愚蠢而没有其他初始化选项(实际上在实际类中)

问题

请帮助我选择其中一种实施方式,并根据您的选择进行推理。

我认为方法#2的主要缺点是客户需要具备专门的 ImmutableList<E> 类型的认知/可见性,可能他们不应该这样做?

3 个答案:

答案 0 :(得分:3)

显然,#3是我认为的最佳选择。 final强制执行并记录该类(该字段)的不变性,而不仅仅是List本身不可变的事实。

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

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

当您的类成员引用Attempt #3无法更改对象的生命周期和/或您希望整个类本身不可变时,

spellings是有意义的。否则,在对象生命周期内可以将spellings分配给另一个ImmutableList,那么它就没有意义。

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