Согласно этой записи в FAQ по 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;
Чем объясняется такое поведение? Как мне интерпретировать различия между сигнатурами этих методов?