Collections.unmodifiableList(...)
返回静态内部类 UnmodifiableList
的新实例。其他不可修改的集合类以相同的方式构造。
这些类是公开的,一个有两个优点:
UnmodifiableList
),因此API用户不会想到修改该集合; List
是instanceof UnmodifiableList
。那么,不是否有任何优势使这些类公开?
编辑:没有提出令人信服的论据,所以我选择了最受欢迎的答案。
答案 0 :(得分:7)
我个人完全同意你的观点。问题的核心是Java的泛型不是协变的,而这反过来又是因为Java的集合是可变的。
Java的类型系统不可能编写一个似乎 mutators 实际上是不可变的的类型。想象一下,如果我们开始设计一些解决方案:
interface Immutable //marker for immutability
interface ImmutableMap<K, V> extends Map<K, V>, Immutable
但是ImmutableMap
是Map
的子类,因此Map
可以从ImmutableMap
分配,因此任何返回这样一个不可变Map的方法:
public ImmutableMap<K, V> foo();
可以分配给Map,因此可以在编译时进行变异:
Map<K, V> m = foo();
m.put(k, v); //oh dear
所以,你可以看到这种类型的添加实际上并没有阻止我们做任何坏事。我认为由于这个原因,我们判断它没有足够的价值。
像 scala 这样的语言具有声明网站差异注释。也就是说,你可以将一个类型指定为协变(因此是不可变的),就像Scala的Map一样(实际上它在V
参数中是协变的)。因此,您的API可以声明其返回类型是可变的还是不可变的。
另外,Scala允许您声明交集类型,以便您甚至不需要将ImmutableXYZ
接口创建为单独的实体,您可以指定要返回的方法:
def foo : XYZ with Immutable
但是scala有一个合适的类型系统,而Java没有
答案 1 :(得分:4)
我认为这两种优势都有,但没有那么有用。主要问题保持不变:UnmodifiableList仍然是List,因此所有setter都可用,底层集合仍然可以修改。将类UnmodifiableList设为public会增加不可修改的错觉。
更好的方法是让编译器提供帮助,但为此,集合类层次结构必须进行大量更改。例如,Scala的集合API在这方面更为先进。
缺点是在API中引入至少三个额外的类/接口。由于它们没有那么有用,我认为将它们排除在API之外是一个不错的选择。
答案 2 :(得分:3)
如果您需要检查列表是否是使用Collections.unmodifiableList创建的,那么您可以创建一个实例并请求该类。现在您可以将此类与任何列表的类进行比较。
private static Class UNMODIFIABLE_LIST_CLASS =
Collections.unmodifiableList( new ArrayList() ).getClass();
...
if( UNMODIFIABLE_LIST_CLASS == listToTest.getClass() ){
...
}
答案 3 :(得分:3)
原因的答案非常简单:当时,在1998年,高效的设计有点侧面。人们想到这一点并不是一个优先事项。但是没有真正的,深刻的思考它。
如果你想使用这样的机制,请使用Guava的ImmutableList / Set / Map /...
它们是显式不可变的,使用该库时的一个好习惯不是返回List,而是返回一个ImmutableList。所以你会知道List / Set / Map / ...是不可变的。
示例:
private final ImmutableList constants = ...;
public final ImmutableList<String> getConstants() {
return constants;
}
关于UnmodifiableXxx的设计本身,可以做到以下几点:
public static final class UnmodifiableXxx implements Xxx { // don't allow extend
// static if inside Collections
UnmodifiableXxx (Xxx backend) { // don't allow direct instanciation
...
}
...
}
答案 4 :(得分:1)
- 能够指示更具体的返回值(例如
UnmodifiableList
),因此API用户不会想到修改该集合;
在适当的API中,这应该已经记录在返回不可修改列表的方法的javadoc中。
- 在运行时检查
的能力List
是instanceof UnmodifiableList
。
这样的需求表明实际的问题存在于其他地方。这是代码设计中的一个缺陷。问问自己,您是否曾经需要检查List
是ArrayList
还是LinkedList
的实例?无论是ArrayList
,LinkedList
还是UnmodifiableList
,显然都是在代码写入时间内做出的决定,而不是在代码运行时。如果您遇到问题,因为您正在尝试修改UnmodifiableList
(API开发人员可能已经非常好地证明了这些问题),那么它就是您自己的错误,而不是运行时故障。< / p>
总而言之,没有任何意义。 Collections#unmodifiableXXX()
,synchronizedXXX()
和checkedXXX()
以任何方式执行都不代表具体实现。它们都只是decorators,无论底层的具体实现如何,都可以应用它们。
答案 5 :(得分:0)
假设UnmodifiableList
是公共类。我怀疑它会让程序员陷入虚假的安全感。请注意,UnmodifiableList
是可修改 List
的视图。这意味着UnmodifiableList
的内容仍然可以通过对其基础List
所做的任何更改来更改。一个天真的程序员可能无法理解这种细微差别,可能期望UnmodifiableList
的实例是不可变的。
答案 6 :(得分:0)
我认为答案是因为方法形式正确地知道所使用的泛型,并且不需要额外的编程来传递这些信息,而类形式则需要更多的混乱。 unmodifiableMap
的方法形式有两个浮动泛型参数,它映射到返回类型和传递参数的泛型参数。
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
return new UnmodifiableMap<K,V>(m);
}