Настолько странный! Взгляните код сначала:
public class A {}
public class B extends A {}
public class C extends A {}
public class TestMain {
public <T extends A> void test(T a, T b) {}
public <T extends A> void test(List<T> a, List<T> b) {}
public void test1(List<? extends A> a, List<? extends A> b) {}
public static void main(String[] args) {
new TestMain().test(new B(), new C());
new TestMain().test(new ArrayList<C>(), new ArrayList<C>());
new TestMain().test(new ArrayList<B>(), new ArrayList<C>());
new TestMain().test1(new ArrayList<B>(), new ArrayList<C>());
}
}
Оператор new TestMain().test(new ArrayList<B>(), new ArrayList<C>())
получает ошибку компиляции:
Связанное несоответствие: тест общего метода (T, T) типа TestMain не применим для аргументов
(ArrayList<B>, ArrayList<C>)
. Выведенный типArrayList<? extends A>
не допустимая замена для ограниченного параметра<T extends A>
Однако:
new TestMain().test(new B(), new C()) --> compiled ok
new TestMain().test(new ArrayList<C>(), new ArrayList<C>()) --> compiled ok
new TestMain().test1(new ArrayList<B>(), new ArrayList<C>()) --> compiled ok
Если мы определяем дженерик перед именем метода, это кажется типом второго дженерика List
параметр должен совпасть с параметром первых. Но нет никакого ограничения, если мы определяем универсальный в параметрах.
Действительно ли это - функция или ошибка программы компиляции? Есть ли некоторая документация об этом?
Нет абсолютно никакой ошибки; вы просто неправильно поняли правила выделения подтипов в дженериках.
Поскольку у нас есть B расширяет A
:
B
является подтипом A
экземпляр B
также является экземпляром A
Поскольку массивы Java ковариантны:
B []
является подтипом A []
instanceof B []
также является instanceof A []
Однако универсальные шаблоны Java инвариантны:
Если у вас есть следующее объявление универсального метода:
public <T extends A> void test(List<T> a, List<T> b)
Тогда, как здесь явно указано, a
и b
должны иметь один и тот же тип, List < T>
, для некоторого преобразования захвата параметра типа
.
Поскольку List
и List
представляют собой два разных типа, вы не можете смешивать их в качестве фактических аргументов для test
. Кроме того, несмотря на то, что B
и C
являются подтипами A
, универсальные шаблоны не являются инвариантными, поэтому ни List
, ни Список
- это Список
.
Таким образом,
test(new ArrayList<B>(), new ArrayList<C>()); // error!!! doesn't compile!!!
не компилируется, что является ожидаемым поведением.
java.lang.ArrayStoreException
О правилах типизации универсальных типов:
List animals = new ArrayList ()
? List
отличается от List
, который является отличается от списка >
При использовании super
и extends
:
Java Generics: что такое PECS?
расширяет
потребитель super
» super
и extends
в Java Generics
и
? List расширяет структуры данных Number>
? (НЕЛЬЗЯ!) Фактические общие ошибки:
Это не ошибка, просто дженерики сложны. Попробуйте изменить ваш второй метод тестирования на:
public <T extends A, K extends A> void test(List<T> a, List<K> b) {
По сути, в том, что вы получили, нет типа T, который может удовлетворить то, что вы передаете, в отличие от первого метода, где B и C просто рассматриваются как A. Фактическое название этого поведения от меня ускользает, но в литературе должно быть много примеров.
Короче говоря, даже если B является дочерним от A, List не является дочерним от List.