决定不使用完全泛型的get方法的原因是什么 在java.util接口中。地图< K、V >。

为了澄清问题,该方法的签名为

V get(对象键)

而不是

V get(K键)

我想知道为什么(同样的事情为remove, containsKey, containsValue)。


当前回答

还有一个更重要的原因,它在技术上不能做到,因为它破坏了地图。

Java has polymorphic generic construction like <? extends SomeClass>. Marked such reference can point to type signed with <AnySubclassOfSomeClass>. But polymorphic generic makes that reference readonly. The compiler allows you to use generic types only as returning type of method (like simple getters), but blocks using of methods where generic type is argument (like ordinary setters). It means if you write Map<? extends KeyType, ValueType>, the compiler does not allow you to call method get(<? extends KeyType>), and the map will be useless. The only solution is to make this method not generic: get(Object).

其他回答

正如其他人所提到的,get()等不是泛型的原因是,您正在检索的条目的键不必与传递给get()的对象的类型相同;该方法的规范只要求它们相等。这源于equals()方法如何接受Object作为参数,而不仅仅是对象的相同类型。

Although it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, there are many places in Java where this is not the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So coming back to the example in this question, according to the specification of the method is possible to have a Map<ArrayList, Something> and for me to call get() with a LinkedList as argument, and it should retrieve the key which is a list with the same contents. This would not be possible if get() were generic and restricted its argument type.

原因是包含是由equals和hashCode决定的,它们是Object上的方法,并且都接受Object参数。这是Java标准库的早期设计缺陷。再加上Java类型系统中的限制,它强制依赖于equals和hashCode的任何东西接受Object。

在Java中获得类型安全哈希表和相等性的唯一方法是避免使用Object。equals和Object。hashCode并使用通用的替代品。函数式Java提供的类型类就是为了这个目的:Hash<A>和Equal<A>。提供了HashMap<K, V>的包装器,在其构造函数中接受Hash<K>和Equal<K>。因此,该类的get和contains方法采用类型为K的泛型参数。

例子:

HashMap<String, Integer> h =
  new HashMap<String, Integer>(Equal.stringEqual, Hash.stringHash);

h.add("one", 1);

h.get("one"); // All good

h.get(Integer.valueOf(1)); // Compiler error

我们正在做大的重构,我们错过了这个强类型的get(),以检查我们是否错过了旧类型的get()。

但是我发现了编译时间检查的变通/丑陋的技巧:创建Map接口,强类型get, containsKey, remove…然后放到java中。您的项目的Util包。

你只调用get()就会得到编译错误,…对于错误的类型,其他的编译器似乎都可以(至少在eclipse kepler内部)。

不要忘记在检查构建后删除此接口,因为这不是您在运行时想要的。

还有一个更重要的原因,它在技术上不能做到,因为它破坏了地图。

Java has polymorphic generic construction like <? extends SomeClass>. Marked such reference can point to type signed with <AnySubclassOfSomeClass>. But polymorphic generic makes that reference readonly. The compiler allows you to use generic types only as returning type of method (like simple getters), but blocks using of methods where generic type is argument (like ordinary setters). It means if you write Map<? extends KeyType, ValueType>, the compiler does not allow you to call method get(<? extends KeyType>), and the map will be useless. The only solution is to make this method not generic: get(Object).

I was looking at this and thinking why they did it this way. I don't think any of the existing answers explains why they couldn't just make the new generic interface accept only the proper type for the key. The actual reason is that even though they introduced generics they did NOT create a new interface. The Map interface is the same old non-generic Map it just serves as both generic and non-generic version. This way if you have a method that accepts non-generic Map you can pass it a Map<String, Customer> and it would still work. At the same time the contract for get accepts Object so the new interface should support this contract too.

在我看来,他们应该添加一个新的接口,并在现有的集合上实现这两个接口,但他们决定支持兼容接口,即使这意味着get方法的设计更糟糕。注意,集合本身与现有方法兼容,只有接口不兼容。