我正在尝试确定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);
}
我的问题:
哪种在应用程序中最有意义? [doSomethingOne和getFooOne]或[doSomethingTwo和fooTwo]?换句话说,如果你知道你正在使用ImmutableCollections,那么继续来回和执行copyOf(),或者只是在所有地方使用Immutable是有意义的吗?
这些示例是公共方法,可能意味着其他人使用它们。如果方法是私有的并且在内部使用,这些答案中的任何一个都会改变吗?
如果用户尝试向不可变列表添加任何内容,则会引发异常。因为他们可能没有意识到这一点,显然返回一个ImmutableCollection而不是一个Collection会不会更有意义吗?
答案 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
。