Java中的List.of(E... elements)
方法返回的列表确实返回了一个不可变的列表,但是通过查看所创建的列表根本看不到该列表。创建的列表只是抛出一个异常,而不是根本没有显示更改列表的可能性。
我的意思是,List.of(E... elements)
应该返回一个ImmutableList
的extends List
。这样,用户可以决定他是否愿意显示这种不变性的事实。
但我没有发现有人提示或显示其他解决方案。默认情况下,甚至Guava和Apache Commons也不会这样做。只有Guava才有可能创建它(尽管有很多代码):
List<String> list = new ArrayList<String>(Arrays.asList("one", "two", "three"));
ImmutableList<String> unmodifiableList = ImmutableList.<String>builder().addAll(list).build();
但是,即使此类也具有(不建议使用)
add
和remove
方法。谁能告诉我为什么没人关心这个(看似基本的)问题?
最佳答案
不是没有人在乎;这是一个相当微妙的问题。
没有“不可变”的收集接口(interface)族的最初原因是因为对interface proliferation的关注。可能不仅存在用于不变性的接口(interface),而且可能存在同步的和运行时类型检查过的集合,还有可能设置了元素但未添加或删除(例如,Arrays.asList)的集合,或者可以从中删除但不添加元素的集合(例如, ,Map.keySet)。
但是也可以说,不变性是如此重要,以至于应采用特殊情况,并且即使不支持所有其他特征,类型层次结构中也应对此提供支持。很公平。
最初的建议是让ImmutableList
接口(interface)扩展List
,如
(其中<:
表示"is"的子类型。)
当然可以做到这一点,但是ImmutableList
将从List
继承所有方法,包括所有mutator方法。他们将不得不做些事情。子接口(interface)无法从 super 接口(interface)“取消继承”方法。最好的办法是指定这些方法抛出异常,提供默认的实现,并可能将这些方法标记为已弃用,以便程序员在编译时得到警告。
这行得通,但是并没有太大帮助。完全不能保证此类接口(interface)的实现是不可变的。恶意或错误的实现可能会覆盖mutator方法,或者可能只是添加更多使状态发生变化的方法。任何使用ImmutableList
的程序都不能做任何假设,认为该列表实际上是不可变的。
对此的一种变体是使ImmutableList
成为类而不是接口(interface),定义其mutator方法以引发异常,使其成为最终对象,并且不提供任何公共(public)构造函数以限制实现。实际上,这正是Guava的 ImmutableList
所做的。如果您信任Guava开发人员(我认为他们非常有信誉),那么如果您有Guava ImmutableList
实例,那么可以确信它实际上是不可变的。例如,您可以将其存储在一个字段中,并且要知道它不会意外从您的下方变成。但这也意味着您不能添加另一个ImmutableList
实现,至少不能不修改Guava而已。
用这种方法不能解决的问题是通过向上转换来“擦洗”不变性。许多现有的API都使用Collection
或Iterable
类型的参数来定义方法。如果将ImmutableList
传递给这样的方法,它将丢失表明列表是不可变的类型信息。要从中受益,您必须在各处添加不可变口味的重载。或者,您可以在各处添加instanceof
检查。两者都很困惑。
(请注意,JDK的 List.copyOf
避免了这个问题。即使没有不可变的类型,它也会在制作副本之前检查实现,并避免不必要地制作副本。因此,调用者可以使用List.copyOf
进行防御性副本,而不受惩罚。)
另一种选择是,有人可能会争辩说我们不希望ImmutableList
成为List
的子接口(interface),我们希望它成为超接口(interface):
这样,无需ImmutableList
来指定所有那些mutator方法都抛出异常,它们根本就不会出现在接口(interface)中。很好,只不过该模型完全错误。由于ArrayList
是List
,这意味着ArrayList
也是ImmutableList
,这显然是荒谬的。问题在于“不可变”意味着对子类型的限制,而这在继承层次结构中是无法做到的。相反,它需要重命名以允许在层次结构中添加功能时添加功能,例如,
比较准确。但是,ReadableList
与ImmutableList
完全不同。
最后,还有许多我们尚未考虑的语义问题。一个涉及不变性与不可修改性。 Java具有支持不可修改的API,例如:
List<String> alist = new ArrayList<>(...);
??? ulist = Collections.unmodifiableList(alist);
ulist
的类型应该是什么?它不是一成不变的,因为如果有人更改后备列表alist
,它将改变。现在考虑:???<String[]> arlist = List.of(new String[] { ... }, new String[] { ... });
类型应该是什么?它肯定不是不可变的,因为它包含数组,并且数组始终是可变的。因此,尚不清楚说
List.of
返回不可变的内容是否合理。关于java - 为什么Java中的List.of()不返回类型化的不可变列表?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57921251/