Java несколько ошибок компиляции параметров универсального набора

Настолько странный! Взгляните код сначала:

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 параметр должен совпасть с параметром первых. Но нет никакого ограничения, если мы определяем универсальный в параметрах.

Действительно ли это - функция или ошибка программы компиляции? Есть ли некоторая документация об этом?

8
задан Mr_and_Mrs_D 28 September 2013 в 10:58
поделиться

2 ответа

Нет абсолютно никакой ошибки; вы просто неправильно поняли правила выделения подтипов в дженериках.

Поскольку у нас есть 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!!!

не компилируется, что является ожидаемым поведением.

См. Также

Связанные вопросы

О правилах типизации универсальных типов: