Подтвердить что ты не робот

Компиляция общего класса в Java 6, но не Java 7

У меня есть интерфейс в 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, но ошибка все же возникает. Изменение типа возврата моего put является спорным, поскольку эта ошибка блокирует возможную ошибку из когда-либо достигнутой, и если эта ошибка была исправлена, тогда оба метода не имели бы одинакового имени/параметра. >

Я думаю, что я мог бы попробовать некоторое размышление над Java 6 и посмотреть, какие фактические типы параметров заканчиваются в скомпилированном байт-коде Java. Понятно, что оба метода Java 7 стираются до put(Object, Object). Я опубликую результаты отражения здесь, когда я это сделаю.

Тем временем мое временное обходное решение будет просто переименовать put в putSingle, но верно ли это новое поведение? Изменилась ли какая-то часть спецификаций дженериков для Java 7, что делает неправильное поведение старого Java 6? Или это ошибка в компиляторе Java 7?

Спасибо заранее.

EDIT: Я запустил код отражения. Посмотрите мой ответ ниже.

4b9b3361

Ответ 1

Я думаю, что это ошибка в версии 1.6, которая была исправлена ​​в 1.7. Выдержка из этой страницы:

Сводка: Класс не может определить два метода с одинаковой стираемой подписью, но два разных типа возврата
Описание: Класс не может определить два метода с одинаковой стертой сигнатурой, независимо от того, являются ли типы возврата одинаковыми или нет. Это следует из 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; }
}

Этот код компилируется под JDK 5.0 и JDK 6 и отклоняется в JDK 7.

Ответ 2

Я запустил код отражения на 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)

Таким образом, они стираются до тех же параметров с другим типом возврата. Я предполагаю, что это ошибка неопределенного случая кромки в Java 6 JLS, тогда, как и ответ assylias. Интересно, как Java 6 удалось правильно разрешить эти методы во время выполнения?

Изменить: Согласно комментарию x4u, вызывающий байт-код поддерживает ссылку на всю подпись при компиляции, поэтому, почему JVM вызывал правильный метод. Поскольку компилятор, вероятно, мог рассказать, какой метод я вызывал из-за его доступа к источнику (и, следовательно, к информации генериков), компилятор, вероятно, связал его с правильным методом с помощью всей сигнатуры. Интересно знать!