Компиляция общего класса в 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: Я запустил код отражения. Посмотрите мой ответ ниже.
Ответы
Ответ 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 вызывал правильный метод. Поскольку компилятор, вероятно, мог рассказать, какой метод я вызывал из-за его доступа к источнику (и, следовательно, к информации генериков), компилятор, вероятно, связал его с правильным методом с помощью всей сигнатуры. Интересно знать!