17

正しくコンパイルされるJava6のインターフェイスがあります。

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ましたが、それでもエラーが発生します。putこのエラーが潜在的なエラーに到達するのをブロックしているため、myの戻りタイプを変更することは重要ではありません。このエラーが修正された場合、2つのメソッドはとにかく同じ名前/パラメーターシグネチャを持ちません。

Java 6について少し考えてみて、実際のパラメーターの種類がJava6のコンパイル済みバイトコードにどのようになるかを確認すると思います。両方のJava7メソッドがに消去されていることは明らかですput(Object, Object)。それができたら、ここにリフレクションの結果を投稿します。

それまでの間、一時的な回避策は名前をに変更するputことputSingleですが、この新しい動作は正しいですか?Java 7のジェネリック仕様の一部が変更され、古いJava 6の動作が正しくなくなりましたか?それとも、これはJava 7コンパイラのバグですか?

前もって感謝します。

編集:リフレクションコードを実行しました。以下の私の答えをチェックしてください。

4

2 に答える 2

19

1.7で修正された1.6のバグだと思います。このページからの抜粋:

概要:クラスは、同じ消去されたシグニチャを持つ2つのメソッドを定義できませんが、2つの異なる戻りタイプ
説明:戻りタイプが同じかどうかに関係なく、クラスは同じ消去されたシグニチャを持つ2つのメソッドを定義できません。これは、JLS、Java SE 7 Edition、セクション8.4.8.3に続くものです。JDK 6コンパイラでは、消去された署名は同じで、戻り値のタイプが異なるメソッドを使用できます。この動作は正しくなく、JDK 7で修正されています。
例:

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

このコードは、JDK5.0およびJDK6でコンパイルされ、JDK7では拒否されます。

于 2012-08-30T22:14:27.543 に答える
1

I ran the reflection code on Java 6.

Here's the code:

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());
    }
}

Here are the method signatures for the class:

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)

Or more concisely:

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

So they are erased to the same parameters with a different return type. I guess it's a bug an unspecified edge case in the Java 6 JLS then, as per assylias' answer. I wonder how Java 6 managed to resolve these methods correctly on runtime?

Edit: According to x4u's comment, the calling bytecode maintains a reference to the entire signature when it's compiled, so that's why the correct method was being called by the JVM. Since the compiler was probably capable of telling which method I was calling due to its access to the source (and thus to the generics information), the compiler probably linked it to the correct method via the entire signature. Interesting to know!

于 2012-08-30T22:31:19.390 に答える