ArrayList с использованием компилятора addAll() показывает различное поведение с дженериками
Может кто-нибудь объяснить мне следующее поведение?
У меня есть список X
и используйте метод addAll()
для добавления элементов. Эти элементы возвращаются методом, использующим общие типы. Метод getA()
возвращает < T extends A >
, когда A
является классом. Метод getI()
возвращает < T extends I >
с I
, являющимся интерфейсом (см. Код ниже).
Разница: с listX.addAll(getA())
Я получаю ошибку компиляции (как и ожидалось), но listX.addAll(getI())
компилирует (выдает ошибку времени выполнения, когда элемент отличен до X
).
Упрощенный код:
interface I {}
class A implements I {}
class X {}
public void test() {
List<X> listX = new ArrayList<>();
listX.addAll(getA());
listX.addAll(getI());
for (X x : listX) {}
}
public <T extends A> List<T> getA() {
return new ArrayList<>();
}
public <T extends I> List<T> getI() {
return new ArrayList<>();
}
Я что-то упустил? Разве я не должен получать ошибку компиляции в оба раза?
Такое поведение кажется новым с Java 8, с версиями ниже я получил ошибки компилятора в обоих случаях.
Ответы
Ответ 1
Я хотел бы упростить вопрос и ответить Шмозелю следующим образом:
interface I {}
class A implements I {}
class X {}
public void test() {
X temp = getI(); // compiles
X temp2 = getA(); // does not compile
}
public <T extends I> T getI() {
return null;
}
public <T extends A> T getA() {
return null;
}
getI() может потенциально возвращать то, что расширяет X и реализует I, поэтому он компилируется. Обычно тип, который он фактически возвращает, будет зависеть от чего-то, например аргумента, переданного в функцию.
getA() не может вернуть что-то, что является X, так как он возвращает то, что расширяет A, которое не расширяет X.
Ответ 2
listX.addAll(getA());
не компилируется, потому что нет возможного подкласса X
, который также является подклассом A
.
listX.addAll(getI());
выполняет компиляцию, поскольку может существовать подкласс X
, который также реализует I
.