17

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

public interface IMultiMap<K, V> extends Map<K, Set<V>> {

    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,但仍然出现错误。更改 my 的返回类型put没有实际意义,因为此错误阻止了该潜在错误的发生,并且如果此错误已修复,那么这两种方法无论如何都不会具有相同的名称/参数签名。

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

同时,我的临时解决方法只是重命名putputSingle,但这种新行为是否正确?Java 7 的某些泛型规范是否发生了更改,导致旧的 Java 6 行为错误?或者这是 Java 7 编译器中的错误?

提前致谢。

编辑:我运行了反射代码。在下面查看我的答案。

4

2 回答 2

19

我认为这是在 1.7 中修复的 1.6 中的错误。从此页面摘录:

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

class A {
   int m(List<String> ls) { return 0; }
   long m(List<Integer> ls) { return 1; }
}

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

于 2012-08-30T22:14:27.543 回答
1

我在 Java 6 上运行了反射代码。

这是代码:

public static void main(String[] args) {
    Class<IMultiMap> 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 调用正确方法的原因。由于编译器可能能够通过访问源代码(从而访问泛型信息)来判断我正在调用哪个方法,因此编译器可能通过整个签名将其链接到正确的方法。很有趣知道!

于 2012-08-30T22:31:19.390 回答