Подтвердить что ты не робот

Зачем мне явно вводить общий вызов?

Предположим, что у меня есть следующее:

public <T extends Widget> List<T> first(T n) {
    return first(n.getClass());
}
public <T extends Widget> List<T> first(Class<T> n) {
    return new ArrayList<>();
}

Компилятор жалуется на строку 3 с помощью "incompatible types; required: java.util.List<T>; found: java.util.List<capture#1 of ? extends my.app.Widget>". Который я не понимаю, почему. Мне кажется разумным, что тип T никогда не может измениться в любом случае, кроме подтипа.

Это может быть исправлено с помощью явного каста, хотя я не знаю, почему это требуется.

public <T extends Widget> List<T> first(T n) {
    return (List<T>)first(n.getClass());
}
public <T extends Widget> List<T> first(Class<T> n) {
    return new ArrayList<>();
}

Может ли это быть ошибкой компилятора?

Примечание. Я использую JDK 1.7.0_15:

java version "1.7.0_15"
Java(TM) SE Runtime Environment (build 1.7.0_15-b03)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)
4b9b3361

Ответ 1

В точности, как вы заявили, параметр типа действительно может быть супертипом реального типа времени выполнения объекта, в котором вы проходили! T#getClass() не возвращает Class<T>, он возвращает Class<? extends T>

Number number = Integer.valueOf(1);
List<Number> list = first(number);

Когда вы вызываете n.getClass() во время выполнения, возвращается Integer.class, а не Number.class, но вы пытаетесь присвоить результат List<Number>! Компилятор не имеет возможности узнать, каков будет реальный тип времени выполнения, он знает только, что возвращается List<? extends Number>. Заставляя вас положить в литье это его способ сказать: "Я не могу ручаться за безопасность этой операции, это на ваше слово, что это правильно".

В любое время, когда компилятор не может подтвердить, что операция является типичной, она заставит вас выполнить бросок и, таким образом, подвергнуть "непроверенному" предупреждению, чтобы он дал свое задание сообщить вам о проблеме.

Ответ 2

getClass() возвращает значение типа Class<?>. Когда вы передаете его другому переопределению first, вы выполняете непроверенный листинг на Class<? extends Widget>. Он не установлен, поскольку generics используют "стирание типа" во время выполнения, что означает, что JVM не может его проверить; пройдите -Xlint:unchecked, чтобы увидеть это предупреждение.

Обратите внимание, что это не Class<T extends Widget>, что означает, что система типов не гарантирует, что параметр типа этого метода (первое переопределение first) будет таким же, как и тот, который вызывается (другой first).

Итак, результат, который вы возвращаете, имеет тип List<? extends Widget> (где ? extends Widget is capture#1), который несовместим с List<T extends Widget>, и поэтому компилятор корректно создает ошибку.

В этом случае вам известно (хотя компилятор не знает), что это разумная вещь, поэтому вы можете переопределить его с явным приложением. Но в этом случае почему бы просто не сделать этот метод следующим:

public <T extends Widget> List<T> first() {
    return new ArrayList<T>();
}

Ответ 3

Это потому, что n.getClass() всегда возвращает Class<?> вместо Class<T>, поэтому компилятор не может напрямую разрешить это. Вы всегда нуждаетесь здесь, чтобы бросить явно. Например

public <T extends Widget> List<T> first(T n) {
    return first((Class<T>)n.getClass());
}

Ответ 4

Общие и их конечные типы определяются во время компиляции, а не во время выполнения. Во время выполнения вся родовая информация стирается - это называется Тип стирания. Похоже, что здесь вы пытаетесь определить общий тип во время вызова функции, который не поддерживается в Java

public <T extends Widget> List<T> first(T n) {
    return first(n.getClass());
}

Здесь вам нужно вернуть List<T> - экземпляр списка. Конкретный экземпляр должен иметь конкретный параметр T. То есть нельзя вернуть List<T extends Widget> - тип будет неизвестен. Что бы вы сохранили в таком списке?

public <T extends Widget> List<T> first(Class<T> n) {
    return new ArrayList<>();
}

Здесь вы хотите вернуть список на основе поставляемого класса, но посмотрите, что вы на самом деле создаете общий ArrayList, который затем переводится в требуемый тип. Проблема в том, что T здесь не совпадает с T в предыдущем методе. Вам нужно будет объявить весь общий класс с одним T, чтобы сделать их одинаковыми.