Ответ 1
Прежде всего, тело метода вашего примера кода можно упростить, чтобы:
public void traverse() {
MyEntity myEntity = null;
for (String label : myEntity.getLabels()) { // <-- Offending Line
/* ... */
}
}
Почему это происходит? Потому что, когда вы объявляете переменную myEntity
(она не имеет значения, где-in for-loop или как в моем примере) в качестве MyEntity myEntity
, вы объявляете ее как raw, что исключает общий тип из return type метода getLabels
: так что он становится таким же, как List getLabels();
, где, очевидно, тип Object
ожидается для построения цикла.
В то же время Iterator<String> iterator = myEntity.getLabels().iterator();
работает отлично, потому что вы явно указываете тип: Iterator<String>
.
Очень похожий пример приведен в JLS 4.8 "Необработанные типы" , который объясняет, почему это происходит:
... Унаследованные члены типа, зависящие от переменных типа, будут унаследованные как необработанные типы, как следствие того, что правило супертипы сырого типа стираются...
Другое значение вышеприведенных правил состоит в том, что общий внутренний класс исходного типа может использоваться только как необработанный тип:
class Outer<T>{
class Inner<S> {
S s;
}
}
Невозможно получить доступ к Inner как к частично сырым типам ( "редкий" тип):
Outer.Inner<Double> x = null; // illegal
UPD-2. Когда я получил вопросы о Iterator<String> iterator = myEntity.getLabels().iterator();
, почему это нормально, в то время как первый пример не работает?
Я лично согласен, что это выглядит запутанным. Но таковы правила. Этот случай также рассматривается в том же параграфе JLS в этом примере:
class Cell<E> {
E value;
Cell(E v) { value = v; }
E get() { return value; }
void set(E v) { value = v; }
public static void main(String[] args) {
Cell x = new Cell<String>("abc");
System.out.println(x.value); // OK, has type Object
System.out.println(x.get()); // OK, has type Object
x.set("def"); // unchecked warning
}
}
Более подробное объяснение того, почему Iterator<String> iterator = myEntity.getLabels().iterator();
работает из JLS, основывается на этом правиле:
То есть, правила подтипирования (§4.10.2) программирования Java язык позволяют назначать переменную типа raw значение любого из параметрируемых экземпляров типа
Таким же образом вы всегда можете написать хорошо скомпилированный код как:
List<String> labels = myEntity.getLabels();
for (String label : labels) { // <-- OK, no error here
/* ... */
}