简体   繁体   English

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

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

Collections.unmodifiableList(...) returns a new instance of a static inner class UnmodifiableList . Collections.unmodifiableList(...)返回静态内部类 UnmodifiableList的新实例。 Other unmodifiable collections classes are constructed same way. 其他不可修改的集合类以相同的方式构造。

Were these classes public, one had two advantages: 如果公开这些课程,一个有两个好处:

  • ability to indicate a more specific return value (such as UnmodifiableList ), so an API user wouldn't come to the idea of modifying that collection; 能够指示更具体的返回值(例如UnmodifiableList ),因此API用户不会想到修改该集合;
  • ability to check during runtime if a List is instanceof UnmodifiableList . 如果Listinstanceof UnmodifiableList则在运行时检查的能力。

So, were there any advantages not to make those classes public? 那么,是否有任何优势不让这些课程公开?

EDIT : No definitely convincing arguments were presented, so I choose the most upvoted answer. 编辑 :没有提出绝对令人信服的论据,所以我选择了最受欢迎的答案。

Personally I completely agree with you. 我个人完全同意你的意见。 At the core of the problem is that fact that Java's generics are not covariant, which, in turn, is because Java's collections are mutable. 问题的核心是Java的泛型不是协变的,而这反过来又是因为Java的集合是可变的。

It is not possible for Java's type system to codify a type that seems to have mutators is actually immutable . Java的类型系统不可能编写一个似乎有mutator 实际上是不可变的类型 Imagine if we were to start designing some solution: 想象一下,如果我们开始设计一些解决方案:

interface Immutable //marker for immutability

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

But then ImmutableMap is a subclass of Map , and hence Map is assignable from ImmutableMap so any method which returns such an immutable Map: 但是ImmutableMapMap的子类,因此Map可以从ImmutableMap赋值,所以任何返回这种不可变Map的方法:

public ImmutableMap<K, V> foo();

can be assigned to a Map and can therefore be mutated at compile time: 可以分配给Map,因此可以在编译时进行变异:

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

So, you can see that the addition of this type has not actually prevented us from doing anything bad. 所以,你可以看到这种类型的添加实际上并没有阻止我们做任何坏事。 I think for this reason a judgement was made that it did not have enough to offer. 我认为由于这个原因,我们判断它没有足够的价格。


A language like scala has declaration-site variance annotations. scala这样的语言具有声明站点方差注释。 That is, you could specify a type as being covariant (and hence immutable) as Scala's Map is (actually it's covariant in its V parameter). 也就是说,您可以将类型指定为协变(因此不可变),因为Scala的Map是(实际上它在V参数中是协变的)。 Hence your API can declare whether its return type is mutable or immutable. 因此,您的API可以声明其返回类型是可变的还是不可变的。

As another aside, Scala lets you declare intersection types so that you don't even need to create the ImmutableXYZ interface as a separate entity, you could specify a method to return: 另外,Scala允许您声明交集类型,以便您甚至不需要将ImmutableXYZ接口创建为单独的实体,您可以指定要返回的方法:

def foo : XYZ with Immutable

But then scala has a proper type system, whereas Java does not 但是scala有一个合适的类型系统,而Java没有

I think both advantages are there but are not that useful. 我认为这两个优点都有,但没有那么有用。 The main problems remain the same: UnmodifiableList still is a List and thus all the setters are available and the underlying collections still are modifiable. 主要问题保持不变:UnmodifiableList仍然是List,因此所有setter都可用,底层集合仍然可以修改。 Making the class UnmodifiableList public would add to the illusion of being unmodifiable. 使类UnmodifiableList公开会增加不可修改的错觉。

The nicer way would be for the compiler to help, but for that the collection class hierarchies would have to changed a lot. 更好的方法是让编译器提供帮助,但是为此,集合类层次结构必须改变很多。 Eg, the collection API of Scala is way more advanced in that respect. 例如,Scala的集合API在这方面更先进。

A disadvantage would be the introduction of at least three additional classes / interfaces into the API. 缺点是在API中引入至少三个附加类/接口。 Because of them not being that useful, I think leaving them out of the API is a good choice. 由于它们没有那么有用,我认为将它们排除在API之外是一个不错的选择。

If it important for you to check if the list was created with Collections.unmodifiableList then you can create an instance and ask for the class. 如果您需要检查列表是否是使用Collections.unmodifiableList创建的,那么您可以创建一个实例并请求该类。 Now you you can compare this class with the class of any list. 现在您可以将此类与任何列表的类进行比较。

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

The answer to the why is quite simple: at the time, in 1998, efficient design was a bit flanky. 答案的原因很简单:当时,1998年,高效的设计有点侧面。 People thought about it it wasn't apparently a priority. 人们想到这一点并不是一个优先事项。 But there was no true, deep thinking about it. 但是没有真正的,深刻的思考。

If you want to use such a mechanism, use Guava's ImmutableList/Set/Map/... 如果你想使用这样的机制,使用Guava的ImmutableList / Set / Map / ...

They are explicitly Immutable and a good practice when using that library is not to return a List for instance but an ImmutableList. 它们是显式不可变的,并且使用该库时的一个好习惯不是返回一个List,例如一个ImmutableList。 So you will know that a List/Set/Map/... is immutable. 所以你会知道List / Set / Map / ...是不可变的。

Example: 例:

private final ImmutableList constants = ...;
public final ImmutableList<String> getConstants() {
  return constants;
}

About the design itself of UnmodifiableXxx, one could have done the following: 关于UnmodifiableXxx的设计本身,可以做到以下几点:

public static final class UnmodifiableXxx implements Xxx { // don't allow extend
  // static if inside Collections
  UnmodifiableXxx (Xxx backend) { // don't allow direct instanciation
    ...
  }
  ...
}
  • ability to indicate a more specific return value (such as UnmodifiableList ), so an API user wouldn't come to the idea of modifying that collection; 能够指示更具体的返回值(例如UnmodifiableList ),因此API用户不会想到修改该集合;

In a proper API, this should already be documented in the javadoc of the method returning the unmodifiable list. 在适当的API中,这应该已经记录在返回不可修改列表的方法的javadoc中。

  • ability to check during runtime if a List is instanceof UnmodifiableList . 如果Listinstanceof UnmodifiableList则在运行时检查的能力。

Such a need indicates that the actual problem lies somewhere else. 这种需要表明实际问题存在于其他地方。 It's a flaw in the code design. 这是代码设计中的一个缺陷。 Ask yourself, have you ever had the need to check if a List is an instance of ArrayList or LinkedList ? 问问自己,您是否曾经需要检查List是否是ArrayListLinkedList的实例? Whether it's an ArrayList , LinkedList or UnmodifiableList is clearly a decision which is to be made during code write time, not during code run time. 无论是ArrayListLinkedList还是UnmodifiableList ,显然都是在代码写入时间内做出的决定,而不是在代码运行时。 If you're encountering problems because you're attempting to modify an UnmodifiableList (for which the API developer may have very good reasions which should be already documented), then it's rather your own fault, not a runtime fault. 如果您遇到问题,因为您正在尝试修改UnmodifiableList (API开发人员可能已经记录了非常好的版本),那么这是您自己的错,而不是运行时错误。

All with all, it makes no sense. 总而言之,没有任何意义。 The Collections#unmodifiableXXX() , synchronizedXXX() and checkedXXX() do in any way not represent concrete implementations. Collections#unmodifiableXXX() checkedXXX() Collections#unmodifiableXXX()synchronizedXXX()checkedXXX()以任何方式执行并不代表具体实现。 They are all just decorators which can be applied regardless of the underlying concrete implementation. 它们都只是装饰器 ,无论底层具体实现如何都可以应用。

Suppose UnmodifiableList was a public class. 假设UnmodifiableList 一个公共类。 I suspect that it would lull programmers into a false sense of security. 我怀疑它会让程序员陷入虚假的安全感。 Remember, UnmodifiableList is a view of a modifiable List . 请记住, UnmodifiableList可修改 List视图 This means that the contents of an UnmodifiableList can still change via any changes made to its underlying List . 这意味着UnmodifiableList的内容仍然可以通过对其基础List进行的任何更改来更改。 A naive programmer may not understand this nuance and may expect instances of UnmodifiableList to be immutable. 一个天真的程序员可能无法理解这种细微差别,并且可能期望UnmodifiableList实例是不可变的。

I think the answer is because the method form properly knows about the generics used and requires no extra programming to pass this information through, whilst the class form would require more messing about. 我认为答案是因为方法形式正确地知道所使用的泛型,并且不需要额外的编程来传递这些信息,而类形式则需要更多的混乱。 The method form for unmodifiableMap has two floating generic arguments, which it maps to both the generic arguments of the return type and of the passed argument. 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