Ошибка компиляции нескольких универсальных параметров коллекции Java - PullRequest
8 голосов
/ 24 мая 2010

Так странно! Пожалуйста, сначала посмотрите код:

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>()) получает ошибку компиляции:

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

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

Ответы [ 2 ]

18 голосов
/ 24 мая 2010

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

Так как B extends A:

  • B является подтипом A
  • и instanceof B также является instanceof A

Поскольку массивы Java ковариантны:

  • B[] является подтипом A[]
  • и instanceof B[] также является instanceof A[]

Тем не менее, дженерики Java являются инвариантами:

  • List<B> НЕ является подтипом List<A>
  • ainstanceof List<B> НЕ является instanceof List<A>.

Когда у вас есть следующее объявление универсального метода:

public <T extends A> void test(List<T> a, List<T> b) 

Тогда, как здесь явно указано, a и b должны оба иметь одинаковый тип, List<T>, для некоторого преобразования захвата параметра типа <T extends A>.

Поскольку List<B> и List<C> - это два разных типа, вы не можете смешивать их как фактические аргументы для test.Кроме того, хотя B и C являются подтипами A, генерики являются инвариантными, поэтому ни List<B>, ни List<C> не является List<A>.

Таким образом,

test(new ArrayList<B>(), new ArrayList<C>()); // error!!! doesn't compile!!!

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

См. Также

Смежные вопросы

По правилам набора дженериков:

Вклс использованием super и extends:

По фактическим общим ошибкам:

9 голосов
/ 24 мая 2010

Это не ошибка, просто дженерики сложны. Попробуйте изменить второй метод тестирования на:

    public <T extends A, K extends A> void test(List<T> a, List<K> b) {

По сути, в том, что у вас есть, нет типа T, который мог бы удовлетворить то, что вы передаете, в отличие от первого метода, где B и C просто рассматриваются как A. Фактическое имя для этого поведения ускользает от меня, но здесь быть много примеров в литературе.

Короче говоря, даже если B является потомком A, List не является потомком List .

...