繁体   English   中英

Java集合API:为什么Unmodifiable [List | Set | Map]不是公开可见的类?

[英]Java collections API: why are Unmodifiable[List|Set|Map] not publicly visible classes?

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

如果公开这些课程,一个有两个好处:

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

那么,是否有任何优势不让这些课程公开?

编辑 :没有提出绝对令人信服的论据,所以我选择了最受欢迎的答案。

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

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

interface Immutable //marker for immutability

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

但是ImmutableMapMap的子类,因此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没有

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

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

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

如果您需要检查列表是否是使用Collections.unmodifiableList创建的,那么您可以创建一个实例并请求该类。 现在您可以将此类与任何列表的类进行比较。

private static Class UNMODIFIABLE_LIST_CLASS = 
    Collections.unmodifiableList( new ArrayList() ).getClass();
...
if( UNMODIFIABLE_LIST_CLASS == listToTest.getClass() ){
    ...
} 

答案的原因很简单:当时,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
    ...
  }
  ...
}
  • 能够指示更具体的返回值(例如UnmodifiableList ),因此API用户不会想到修改该集合;

在适当的API中,这应该已经记录在返回不可修改列表的方法的javadoc中。

  • 如果Listinstanceof UnmodifiableList则在运行时检查的能力。

这种需要表明实际问题存在于其他地方。 这是代码设计中的一个缺陷。 问问自己,您是否曾经需要检查List是否是ArrayListLinkedList的实例? 无论是ArrayListLinkedList还是UnmodifiableList ,显然都是在代码写入时间内做出的决定,而不是在代码运行时。 如果您遇到问题,因为您正在尝试修改UnmodifiableList (API开发人员可能已经记录了非常好的版本),那么这是您自己的错,而不是运行时错误。

总而言之,没有任何意义。 Collections#unmodifiableXXX() checkedXXX() Collections#unmodifiableXXX()synchronizedXXX()checkedXXX()以任何方式执行并不代表具体实现。 它们都只是装饰器 ,无论底层具体实现如何都可以应用。

假设UnmodifiableList 一个公共类。 我怀疑它会让程序员陷入虚假的安全感。 请记住, UnmodifiableList可修改 List视图 这意味着UnmodifiableList的内容仍然可以通过对其基础List进行的任何更改来更改。 一个天真的程序员可能无法理解这种细微差别,并且可能期望UnmodifiableList实例是不可变的。

我认为答案是因为方法形式正确地知道所使用的泛型,并且不需要额外的编程来传递这些信息,而类形式则需要更多的混乱。 unmodifiableMap的方法形式有两个浮动泛型参数,它映射到返回类型和传递参数的泛型参数。

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
    return new UnmodifiableMap<K,V>(m);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM