Java collections API:为什么是不可修改的[List|套餐|Map] 不是公开可见的类?

2022-09-01 23:36:35

Collections.unmodifiableList(...)返回静态内部类 的新实例。其他不可修改的集合类也是以相同的方式构造的。UnmodifiableList

如果这些类是公开的,其中一个有两个优点:

  • 能够指示更具体的返回值(例如),因此API用户不会想到修改该集合;UnmodifiableList
  • 能够在运行时检查 a 是否为 。Listinstanceof UnmodifiableList

那么,公开这些课程有什么好处吗?

编辑:没有提出绝对令人信服的论点,所以我选择投票最多的答案。


答案 1

就我个人而言,我完全同意你的看法。问题的核心是Java的泛型不是协变的,这反过来又是因为Java的集合是可变的。

Java的类型系统不可能编纂一个似乎有突变体的类型实际上是不可变的。想象一下,如果我们开始设计一些解决方案:

interface Immutable //marker for immutability

interface ImmutableMap<K, V> extends Map<K, V>, Immutable

但是 then 是 的子类,因此可以从 so 任何返回这样一个不可变 Map 的方法进行赋值:ImmutableMapMapMapImmutableMap

public ImmutableMap<K, V> foo();

可以分配给 Map,因此可以在编译时进行更改:

Map<K, V> m = foo();
m.put(k, v); //oh dear

因此,您可以看到,添加此类型实际上并没有阻止我们做任何坏事。我认为,出于这个原因,有人作出判断,认为它没有足够的能力提供。


scala 这样的语言具有声明站点方差注释。也就是说,您可以将类型指定为协变(因此不可变),就像Scala的Map一样(实际上它的参数是协变的)。因此,您的 API 可以声明其返回类型是可变的还是不可变的。V

另一方面,Scala允许您声明交集类型,这样您甚至不需要将接口创建为单独的实体,您可以指定一个要返回的方法:ImmutableXYZ

def foo : XYZ with Immutable

但是Scala有一个合适的类型系统,而Java没有。


答案 2

我认为这两个优点都存在,但没有那么有用。主要问题保持不变:UnmodifiableList仍然是一个列表,因此所有集合都可用,底层集合仍然是可修改的。公开“不可修改列表”类会增加不可修改的错觉。

更好的方法是让编译器提供帮助,但为此,集合类层次结构必须发生很大变化。例如,Scala的集合API在这方面要先进得多。

缺点是至少在API中引入三个额外的类/接口。由于它们不是那么有用,我认为将它们排除在API之外是一个不错的选择。