В соответствии с этой записью в Часто задаваемых вопросах Java Generics, есть некоторые обстоятельства, когда общий метод не имеет эквивалентного не общего метода, который использует подстановочный знак типы. Согласно этому ответу,
Если сигнатура метода использует многоуровневые подстановочные типы, всегда существует разница между сигнатурой общего метода и версией подстановочного знака.
Они приводят пример метода <T> void print1( List <Box<T>> list)
, который "требует список ящиков того же типа". Подстановочная версия void print2( List <Box<?>> list)
, "принимает гетерогенный список ящиков разных типов" и, следовательно, не эквивалентна.
Как вы интерпретируете различия между следующими двумя сигнатурами метода:
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
Интуитивно, кажется, что эти определения должны быть эквивалентными. Однако вызов f(ArrayList.class)
компилируется с использованием первого метода, но вызов g(ArrayList.class)
с использованием второго метода приводит к ошибке времени компиляции:
g(java.lang.Class<? extends java.lang.Iterable<?>>) in Test
cannot be applied to (java.lang.Class<java.util.ArrayList>)
Интересно, что обе функции можно вызывать с аргументами друг друга, потому что следующие компиляции:
class Test {
<T extends Iterable<?>> void f(Class<T> x) {
g(x);
}
void g(Class<? extends Iterable<?>> x) {
f(x);
}
}
Используя javap -verbose Test
, я вижу, что f()
имеет общую сигнатуру
<T::Ljava/lang/Iterable<*>;>(Ljava/lang/Class<TT;>;)V;
и g()
имеет общую сигнатуру
(Ljava/lang/Class<+Ljava/lang/Iterable<*>;>;)V;
Чем объясняется это поведение? Как интерпретировать различия между сигнатурами этих методов?