2011-03-25 3 views
7

У меня возникла странная ошибка компилятора при использовании генериков в цикле for-each в Java. Является ли это ошибкой компилятора Java, или я действительно что-то пропустил?Почему компилятор Java жалуется на использование foreach с сырым типом?

Вот весь мой класс:

public class Generics<T extends Object> { 
    public Generics(T myObject){ 
    // I didn't really need myObject 
    } 

    public List<String> getList(){ 
    List<String> list = new ArrayList<String>(); 
    list.add("w00t StackOverflow"); 
    return list; 
    } 

    public static void main(String...a){ 
    Generics generics = new Generics(new Object()); 
    for(String s : generics.getList()){ 
     System.out.println(s); 
    } 
    } 
} 

Компилятор жалуется на линии с для-каждого: «Несоответствие типа не может преобразовать из типа элемента объекта в строку.»
Если бы я сделать это тонкое изменение, оно составляет:

public static void main(String...a){ 
    Generics<?> generics = new Generics(new Object()); 
    for(String s : generics.getList()){ 
    System.out.println(s); 
    } 
} 

Я знаю, что getList() делает использование дженериков, но он использует их в том, что я думал, что был совершенно не связан способом. Я мог это понять, если бы пытался перебрать что-то типа T и getList() вернул List<T> или что-то в этом роде, но здесь это не так. Тип возврата getList() не должен иметь абсолютно никакого отношения к T, и не должно волновать, использую ли я тип raw для моего объекта Generics или нет ... правильно? Разве это не должно быть совершенно несвязанным, или я действительно чего-то здесь не вижу?

Обратите внимание, что код также компилируется, если я это делаю, что я думал, что должно было быть эквивалентно первому, а также:

public static void main(String...a){ 
    Generics generics = new Generics(new Object()); 
    List<String> list = generics.getList(); 
    for(String s : list){ 
    System.out.println(s); 
    } 
} 
+1

'' ничем не отличается от '' . Вы не делаете родовую версию своего класса, вы делаете необработанный тип. Что подводит нас к вопросу о том, почему ваш класс является общим в первую очередь? Единственное место, где вы используете T, находится в конструкторе, и вы не используете эту ссылку. – unholysampler

+0

Я использовал '', потому что мне просто нужно что-то для примера. Настоящий код, очевидно, является чем-то другим, и он использует T ... он просто использует T полностью не связанным с 'getList()'. –

+0

, не связанный с вашим вопросом, но я бы сделал конструктором Generics cls), так что вам не нужно создавать экземпляр объекта типа T только для создания этого класса Generics. – MeBigFatGuy

ответ

11

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

List getList() 

Теперь, как почему ваша окончательная версия компилирует - хотя это делает, есть предупреждение, если вы используете -Xlint:

Generics.java:16: warning: [unchecked] unchecked conversion 
    List<String> list = generics.getList(); 
             ^

Это похоже на:

List list = new ArrayList(); 
List<String> strings = list; 

... который также компилирует, но с предупреждением под -Xlint.

Мораль истории: не используйте сырые типы!

+0

Я очень удивлен тем, что * все * общие ссылки внутри подписей участников преобразуются в их необработанные формы. В чем же причина для этого (кроме того, что Солнцу это просто понравилось)? –

+4

@Michael: JLS включает в себя это обсуждение в разделе 4.8 (raw types): «Необработанные типы тесно связаны с подстановочными знаками. Оба основаны на экзистенциальных типах. Необработанные типы можно рассматривать как подстановочные знаки, правила типа которых преднамеренно необоснованны, для размещения взаимодействие с устаревшим кодом ». Другими словами, сырые типы действительно не должны обычно появляться в новом коде, но они пытались избежать компиляции старого кода, даже если он был, по крайней мере, подозрительным. –

+0

Очень интересно. Я уже знал, чтобы не использовать необработанные типы (коллега написал код для объявления переменной), но это подчеркивает, что это действительно может иметь значение. –

3

Изменение линии

Generics generics = new Generics(new Object()); 

в

Generics<?> generics = new Generics<Object>(new Object()); 

Корень вашей проблемы состоит в том, что вы используете raw type, поэтому тип getList способ List, нет List<String>.

+0

Generics НЕ будет общим типом String ... вот и все. Строка не связана с типом Generics. Независимо от T 'getList()' должен возвращать 'List '. –

+0

@ Майкл МакГоуэн, хороший момент. Но должен быть некоторый тип, связанный с параметром type в точке объявления. 'Generics generics = new Generics (...);' будет хорошо, по модулю предупреждение о небезопасном преобразовании. –

+0

@Michael McGowan, обратите внимание, что если бы все, что вы делали, это удалить параметр типа '' из объявления класса, тогда это сработает. –

-1

Я сделал пару корректировок кода. В своем комментарии вы не нуждаетесь в объекте Object, поэтому удалите его, чтобы избежать путаницы.Во-вторых, если Дженерики будет родовым, инициализировать его правильно

Вот что новый главный будет выглядеть

public static void main(String...a){ 
    Generics<String> generics = new Generics<String>(); 
    for(String s : generics.getList()){ 
     System.out.println(s); 
    } 
    } 
+0

Generics НЕ будет общим типом String ... вот и все. Строка не связана с типом Generics. –

+0

Я думаю, вы неправильно поняли мою точку зрения. Если вы посмотрите на код, вы получите метод getList(), возвращающий список . Если бы мы хотели сделать код действительно чистым, мы могли бы лишить часть генериков из кода, кроме метода getList(). вы просили о помощи, чтобы это было скомпилировано, а затем его подход был правильным/неправильным. – Sean

+0

Если вы оставите общее замедление на уровне класса, вы откроете метод getList(), чтобы удалить жесткую кодировку, существующую сейчас. – Sean

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