使用guava immutable集合作为方法参数和/或返回类型

时间:2012-03-01 15:38:58

标签: java guava immutability

我正在尝试确定ImmutableList的最佳实践。以下是一个简单的例子,可以帮助解决我的问题:

例如:

public ImmutableCollection<Foo> getFooOne(ImmutableList<Foo> fooInput){ 
   //.. do some work
   ImmutableList<Foo> fooOther = // something generated during the code
   return fooOther;
}

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   //.. do some work
   List<Foo> fooOther = // something generated during the code
   return ImmutableList.copyOf(fooOther);
}

public void doSomethingOne(){
  ImmutableCollection<Foo> myFoo = getFooOne(myList);
  ...
  someOtherMethod(myFoo);
}

public void doSomethingTwo(){
  Collection<Foo> myFoo = getFooOne(myList);
  ...
  someOtherMethod(myFoo);
}

我的问题:

  1. 哪种在应用程序中最有意义? [doSomethingOne和getFooOne]或[doSomethingTwo和fooTwo]?换句话说,如果你知道你正在使用ImmutableCollections,那么继续来回和执行copyOf(),或者只是在所有地方使用Immutable是有意义的吗?

  2. 这些示例是公共方法,可能意味着其他人使用它们。如果方法是私有的并且在内部使用,这些答案中的任何一个都会改变吗?

  3. 如果用户尝试向不可变列表添加任何内容,则会引发异常。因为他们可能没有意识到这一点,显然返回一个ImmutableCollection而不是一个Collection会不会更有意义吗?

5 个答案:

答案 0 :(得分:18)

一般情况下,明智的做法是不要在声明的返回类型中提交特定的实现,但我们将不可变类型视为异常。声明返回类型Immutable*

的原因有几个
  • 他们记录您正在返回快照,而不是实时视图。
  • 他们记录调用者不能改变结果。
  • 他们记录了保留了插入顺序(在您的使用案例中可能有用,也可能没有用)。
  • 他们记录该集合不包含null
  • 有人可能需要asList()reverse()方法。
  • 如果他希望分配到copyOf()字段,您可以为某人Immutable*电话保存。 (但请注意,如果他确实包含copyOf(),即使您没有声明返回类型,它也会为大多数不可变输入短路。)

基本上,我只是来自https://github.com/google/guava/wiki/TenThingsAboutImmutableCollections,你可能想要完整地查看。

答案 1 :(得分:2)

如果我理解你的意图,那么设计getFooXxx以制作一个可变 - 可变列表的不可变副本的正确方法是这样的:

/**
 * Returns an <b>immutable copy</b> of input list.
 */
public ImmutableList<Foo> immutableCopyOfFoo(List<Foo> input){
    return ImmutableList.copyOf(input);
}

为什么?

  • ImmutableList.copyOf() does it's magic当给定列表是不可变的时,
  • 方法签名明确说明了它的作用,
  • 方法返回ImmutableList ,实际上是ImmutableCollection,但为什么要隐藏用户的ImmutableList信息?如果他愿意,他会写Iterable foo = immutableCopyOfFoo(mutableFoo);代替,但99%他会使用ImmtableList,
  • 返回ImmutableList 作出承诺 - &#34;我是不可改变的,如果你试图改变我,我会把所有事情搞砸!&#34; < / em>的
  • 并且最后但并非最不重要 - 在内部使用中提出的方法是不必要的;只需使用

    someOtherMethod(ImmutableList.copyOf(foo));
    

    直接在你的代码中......


您应该检查@ChrisPovirk's answer(并链接到该答案中的wiki)以了解即当List<Foo> input包含null时,如果您尝试制作,您将在运行时获得令人讨厌的NPE不可改变的副本......


编辑回答评论#1:

Collection合同不如List合同严格;即集合不保证元素的任何顺序(&#34;有些是有序的,有些是无序的&#34;)而List有(&#34;有序集合(也称为序列)&#34;) 。

如果输入是列表,则表明订单很重要,因此输出应该保证相同。想象一下:

public ImmutableCollection<Foo> immutableCopyOfFoo(List<Foo> input){
    return ImmutableSortedSet.copyOf(input, someFancyComparator);
}

它闻起来不对劲。如果您不关心订单,那么方法签名应该是immutableCopyOfFoo(Collection<Foo> input)?但这取决于具体的用例。

答案 2 :(得分:1)

public ImmutableCollection<Foo> getFooOne(ImmutableList<Foo> fooInput){
   ImmutableList<Foo> fooOther= fooInput;
   return ImmutableList.copyOf(fooOther);
}

这完全没有意义。你为什么要复制一个不可变的集合?不变性的全部意义在于:它不能被改变,所以你也可以重新使用它。

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   ImmutableList<Foo> fooOther= ImmutableList.copyOf(fooInput);
   return ImmutableList.copyOf(fooOther);
}

???为什么两次???这很好:

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   return ImmutableList.copyOf(fooInput);
}

ImmutableList.copyOf(Collection)非常聪明,可以不修改ImmutableList,并为其他所有内容创建新的ImmutableList

答案 3 :(得分:1)

我通常的做法是:

  • 接受参数列表(因此界面更易于客户使用)

  • 如果性能/内存使用/线程安全很重要,请将提供的List的内容复制到为您的类使用而优化的数据结构

  • 返回ImmutableList时,ImmutableList应该是返回类型(因为它为调用者提供了有关如何使用返回值的更多信息)

  • 当返回List的可变实现时,List应该是返回类型,除非返回类型的其他内容很重要(线程安全,作为坏*示例)

*这是一个糟糕的例子,因为如果您的返回值需要是线程安全的,那么可能意味着您的代码出现了其他问题。

将List / ImmutableList替换为任何不可变的集合类型。

答案 4 :(得分:0)

您应该始终在公共接口上使用标准JRE类。在Guava的Immutable...类上没有额外的方法,因此您没有获得任何编译时安全性:任何对这些对象进行更改的尝试只会在运行时失败(但请参阅Bart的评论)。您应该在返回集合的方法中记录它们是不可变的。

如果您担心并发修改,则应该在公共方法上提供列表的防御性副本,但是可以在私有方法参数上指定ImmutableCollection