Ошибка компилятора дженериков Java: несовместимые типы

При выполнении некоторые не действительно представляют себе вещи с Java, я приехал через ошибку с дженериками, которые я не смог понять, почему он не работает. Код:

package test;
import java.util.*;
public class TestClass {
  public static class A extends C{}
  public static class B extends C{}
  public static class C{}
  public static class D<T>{}
  public static class E<T>{}

  public static void main(String args[]){
      E<D<? extends C>> a = new E<D<A>>();
      E<D<? extends Object>> b = new E<D<? extends C>>();
      E<D<? extends A>> c = new E<D<A>>();
      E<D<? super A>> d = new E<D<A>>();
      D<? extends C> e = new D<A>();
      D<? extends A> f = new D<A>();
      D<? extends A> g = new D<A>();
  }
}

Ошибка, которую я получаю, когда компиляция:

test/TestClass.java:11: incompatible types
found   : test.TestClass.E<test.TestClass.D<test.TestClass.A>>
required: test.TestClass.E<test.TestClass.D<? extends test.TestClass.C>>
      E<D<? extends C>> a = new E<D<A>>();
                            ^
test/TestClass.java:12: incompatible types
found   : test.TestClass.E<test.TestClass.D<? extends test.TestClass.C>>
required: test.TestClass.E<test.TestClass.D<? extends java.lang.Object>>
      E<D<? extends Object>> b = new E<D<? extends C>>();
                                 ^
test/TestClass.java:13: incompatible types
found   : test.TestClass.E<test.TestClass.D<test.TestClass.A>>
required: test.TestClass.E<test.TestClass.D<? extends test.TestClass.A>>
      E<D<? extends A>> c = new E<D<A>>();
                            ^
test/TestClass.java:14: incompatible types
found   : test.TestClass.E<test.TestClass.D<test.TestClass.A>>
required: test.TestClass.E<test.TestClass.D<? super test.TestClass.A>>
      E<D<? super A>> d = new E<D<A>>();
                          ^
4 errors

Если E<D<? extends C>> найден, который должен, конечно, соответствовать E<D<? extends Object>>, право? Или я пропустил что-то?

6
задан rgettman 1 July 2019 в 20:03
поделиться

10 ответов

Возможно, это поможет вам понять:

    ArrayList<Object> aList = new ArrayList<String>();

Это не компилируется с аналогичной ошибкой.

EDIT: Проконсультируйся с: http://www.ibm.com/developerworks/java/library/j-jtp01255.html

2
ответ дан 17 December 2019 в 02:31
поделиться

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

Подумайте об этом примере:

ArrayList<Object> alist = new ArrayList<Number>();

Это не компилируется, потому что это небезопасно. Вы могли бы добавить Strings aList. Вы пытаетесь назначить список объектов, которые гарантированно будут числами, но могут быть любым числом, списку, который гарантирует только то, что вы будете содержать объекты, но это могут быть любые объекты. Если бы компилятор разрешил этот случай, он бы снял ограничение на то, какие типы объектов могут попадать в список. Вот почему вы должны использовать подстановочный знак?, Как таковой:

ArrayList<? extends Object> alist = new ArrayList<Number>();

В компилятор ArrayList , означает "ArrayList определенного типа '?' я не знаю, но знаю, что расширяет Object. Этот список ArrayList гарантированно будет содержать только элементы этого неизвестного символа '?' type, и поэтому содержит только объекты ". Однако в этом случае компилятор не позволит вам выполнить alist.add (2). Почему это так, поскольку компилятор не знает тип элементов списка, и не можете гарантировать, что вам разрешено вставлять в него объекты Integer.

Вы правы, думая, что D является супертипом D . Однако List > не является подтипом List > , вы должны использовать List > .

Ваш случай в основном эквивалентен

ArrayList<D<? extends Object>> alist = new ArrayList<D<? extends C>>();

У вас та же проблема, что и выше,

2
ответ дан 17 December 2019 в 02:31
поделиться

Проверить тип объекта, возвращаемого вызовом Arrays.asList. Я бы предположил , что он возвращает List >, который не может быть преобразован в List >.

1
ответ дан 17 December 2019 в 02:31
поделиться

определяет верхние границы типа, что означает, что тип должен быть расширен от C. , по-видимому, является более общим, поэтому он несовместим.

Подумайте об этом варианте использования. Указав верхнюю границу, вы ожидаете, что будет реализован определенный минимальный интерфейс.

1
ответ дан 17 December 2019 в 02:31
поделиться

Это еще хуже. Вы даже не можете этого сделать:

List<D<?>> lDQ = Arrays.asList(new D<A>());

Это станет более понятным, если вы измените свою программу следующим образом:

List<D<? extends C>> a = Arrays.asList(new D<A>(), new D<B>()); //compiles
List<D<? extends Object>> b = a; //error

По сути, вы объявляете b как нечто, что может принимать общее содержимое ( D < something > ), но список, который вы ему назначаете, принимает только более конкретное содержимое ( D < ближайший общий суперкласс A и B > ).

Объявление b как List > подразумевает, что вы можете, скажем, b.add (new D ()) , но на самом деле это List > , поэтому вы не можете.

0
ответ дан 17 December 2019 в 02:31
поделиться

Нет.
- это не то же самое, что
Если бы это было так, это привело бы также к (неустановленному) предположению - C расширяет объект, и привело бы к тому, что ...

class C {public void doSomethingMEaningful (); };

Список allAtSea = new ArrayList <...> (); Список allObjects = new ArrayList <...> (); allObjects.add (новое целое число (88)); ...

allAtSea.addAll (allObjects); ...

allAtSea.get (...). DoSomethingMeaningful (...); // Ой-ой .. это находит целое число 88


FAQ по C ++ предоставляет наглядный пример для этого в 21.3

-1
ответ дан 17 December 2019 в 02:31
поделиться

Вы не можете наследовать в параметре generics. Предположим, у вас есть следующие ссылки:

List<Object> a;
List<String> b;

Теперь вы назначаете обоим один и тот же список: a = b;

Если какой-то код выполняет следующее:

a.add(new Integer(1));

Что произойдет, если кто-то сделает следующее:

String s = b.get(0);

Вы получите целое число списка строк. Это не должно работать. Вот почему List и List несовместимы, хотя объектной ссылке можно присвоить String. Дженерики не работают с наследованием.

0
ответ дан 17 December 2019 в 02:31
поделиться

Я думаю, что это станет более понятным, если мы расширим ваш пример. Давайте добавим некоторые функции в E и подробно остановимся на первой строке вашего основного метода:

public static class E<T>{
    private final T thing;

    public void setThing(T thing) {
        this.thing = thing;
    }

    public T getThing() {
        return thing;
    }
}

public static void main(String[] args) {
    E<D<? extends C>> a1;
    E<D<A>> a2 = new E<D<A>>();
    a1 = a2; // this won't compile, but why?

    // these things are permissible on a1:
    a1.setThing(new D<A>());
    a2.setThing(new D<B>());

    // now let's try doing the same thing to a2:
    a2.setThing(new D<A>());
    a2.setThing(new D<B>()); // oops
}

Последняя строка нового основного метода объясняет, почему a1 не может быть установлено в a2. Если компилятор позволяет вам это сделать, то вы сможете поместить D в контейнер, который был объявлен как содержащий только D .

Причина, по которой это не очевидно из вашего исходного примера, заключается в потому что вы не создали отдельную переменную, которая ссылалась на объект, используя более строгую типизацию E >. Но, надеюсь, теперь вы видите, что если такая ссылка существует,

0
ответ дан 17 December 2019 в 02:31
поделиться

Дело в том, что List > в основном определяет новый класс, как и List > . Даже когда c расширяет Object, это не означает, что List > расширяет Список > , и это необходимо для работы задания.

Несмотря на то, что эта серия статей написана для платформы .NET, описание проблемы по-прежнему актуально для универсальных Java-шаблонов.

0
ответ дан 17 December 2019 в 02:31
поделиться

При присвоении переменной ( E ) с универсальным типом без подстановочных знаков T , назначаемый объект должен иметь точно T в качестве своего универсального типа (включая все параметры универсального типа T , подстановочный знак и без подстановочного знака). В вашем случае T - это D , что отличается от типа D .

Что вы можете сделать, потому что D может быть назначен D , используется тип подстановочного знака:

E<? extends D<? extends C>> a = new E<D<A>();
1
ответ дан 17 December 2019 в 02:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: