通用类在Java 6中编译,但不在Java 7中编译

我在Java 6中有一个正确编译的接口:

public interface IMultiMap extends Map<K, Set> { public int valueSize(); public boolean put(K key, V value); public void clear(Object key); public boolean isEmpty(Object key); } 

但在Java 7中,此接口无法编译。 我在boolean put(K, V)上得到一个编译错误,它与V put(K, V)具有相同的擦除。 编译器的完整错误:

 error: name clash: put(K#1,V#1) in IMultiMap and put(K#2,V#2) in Map have the same erasure, yet neither overrides the other public boolean put(K key, V value); where K#1,V#1,K#2,V#2 are type-variables: K#1 extends Object declared in interface IMultiMap V#1 extends Object declared in interface IMultiMap K#2 extends Object declared in interface Map V#2 extends Object declared in interface Map 

对于记录,添加任何类型的覆盖都不起作用。 我尝试明确覆盖Map.put ,但错误仍然出现。 更改put的返回类型是没有实际意义的,因为此错误阻止了潜在的错误,并且如果此错误得到修复,那么这两个方法无论如何都不会具有相同的名称/参数签名。

我想我可能会尝试对Java 6进行一些反思,看看实际的参数类型最终会出现在Java 6的编译字节码中。 很明显,两种Java 7方法都被删除put(Object, Object) 。 一旦我这样做,我会在这里发布reflection结果。

与此同时,我的临时解决方法只是将put重命名为putSingle ,但这个新行为是否正确? Java 7中某些部分的generics规范是否发生了变化,导致旧的Java 6行为错误? 或者这是Java 7编译器中的错误?

提前致谢。

编辑:我运行了reflection代码。 看看我的答案如下。

我认为这是1.6中的一个错误,修正了1.7。 摘自此页面 :

概要:类不能定义具有相同擦除签名但具有两种不同返回类型的两种方法
描述:无论返回类型是否相同,类都无法定义具有相同擦除签名的两个方法。 这是从JLS,Java SE 7 Edition,第8.4.8.3节开始的。 JDK 6编译器允许具有相同擦除签名但返回类型不同的方法; 此行为不正确,已在JDK 7中修复。
例:

 class A { int m(List ls) { return 0; } long m(List ls) { return 1; } } 

此代码在JDK 5.0和JDK 6下编译,在JDK 7下被拒绝。

我在Java 6上运行了reflection代码。

这是代码:

 public static void main(String[] args) { Class immClass = IMultiMap.class; Method[] methods = immClass.getMethods(); for (Method method : methods) { if (method.getName().equals("put")) System.out.println(method.toString()); } } 

以下是该类的方法签名:

 public abstract boolean IMultiMap.put(java.lang.Object,java.lang.Object) public abstract java.lang.Object java.util.Map.put(java.lang.Object,java.lang.Object) 

或者更简洁:

 boolean put(Object, Object) Object put(Object, Object) 

因此,它们将被删除为具有不同返回类型的相同参数。 根据assylias的回答,我猜这是Java 6 JLS中一个未指定边缘情况的错误 。 我想知道Java 6如何在运行时正确地解决这些方法?

编辑:根据x4u的注释,调用字节码在编译时保持对整个签名的引用,这就是为什么JVM调用正确的方法的原因。 由于编译器可能因为访问源(以及generics信息)而能够告诉我正在调用哪个方法,因此编译器可能通过整个签名将其链接到正确的方法。 有趣的知道!