2015-07-14 3 views
3

Вот небольшая часть кода, и я не понимаю, почему javac не может ее скомпилировать. Чего я скучаю? Есть ли ошибки?Почему этот фрагмент кода не компилируется

public class HelloWorld<T> { 
    private static enum Type { 
    } 

    private T value; 
    private List<Type> types = new ArrayList<>(); 

    public T getValue() { return value; } 

    public List<Type> getTypes() { return types; } 

    public static void main(String[] args) { 
     for (Type type : new HelloWorld().getTypes()) { // Error: Type mismatch: cannot convert from element type Object to HelloWorld.Type 

     } 
    } 
} 

Почему getTypes() возвращающий (необработанный) список Object когда должен Type список?

Link to online compiler

+0

... вы связались с онлайн-компилятором, но не прочитали его вывод? 'HelloWorld.java:17: ошибка: несовместимые типы: объект не может быть преобразован в Type' - это ошибка, которую он дает. – Dragondraikk

+2

Я видел ошибку. Почему это происходит? GetTypes() не использует общий тип параметра. Тип возвращаемого метода задается явно. – Mirian

+1

Тот факт, что «Тип» является приватным, и «getTypes» публично возвращает список этих частных объектов, он не вызывает никого, кроме меня? – Orace

ответ

3

Это выглядит как ограничение компилятора для меня. getTypes всегда возвращает List<Type>, поэтому использование необработанного типа HelloWorld не должно иметь значения.

Тем не менее, любой из этих двух решений будет преодолеть ошибку:

  1. Создать параметризованный тип HelloWorld вместо типа сырой:

    for (Type type : new HelloWorld<Integer>().getTypes()) { // any type will do, I chose 
                      // Integer arbitrarily to show 
                      // that it doesn't matter 
    
    } 
    
  2. Использование локальной переменной для хранения Перечень перед его использованием:

    List<Type> types = new HelloWorld().getTypes(); 
    for (Type type : types) { 
    
    } 
    

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

+1

Или даже без какого-либо параметра работает нормально, например 'new HelloWorld <>(). GetTypes();' – Codebender

+0

@Codebender Я не тестировал это, так как для него требуется Java 7 или выше. 'new HelloWorld () .getTypes()' также работает, что смешно. – Eran

+0

@ Ваши предложения работают! Но я хотел бы понять, это обходное решение или типичное использование классов с дженериками? Речь идет о первом случае. Потому что для меня непонятно, почему поле (типы), объявленное без общей информации, каким-то образом зависит от общего объявления при создании экземпляра класса. Похоже, <> делает больше магии, чем раньше. – Mirian

0

изменения for петли для добавления универсального типа для класса:

для например: Я просто использовал «Type» в качестве примера.

for (Type type : new HelloWorld<Type>().getTypes()) { 

Edit:

Это может быть 'Строка', а также отметил в комментариях (спасибо за это).. В вашем случае это должен быть фактический тип, который вам нужен для этого класса.

Идея состоит в том, что отсутствовал общий тип, который необходимо добавить.

+1

Хотя этот ответ может быть правильным, вы также должны объяснить, почему ошибка и почему ваш ответ исправит ее. Это поможет OP больше. – Codebender

+1

Типичный параметр типа T не используется нигде в методе 'getTypes', так почему это имеет значение? 'getTypes' должен возвращать' List 'независимо от параметра типового типа (' new HelloWorld () .getTypes() 'все равно должен возвращать« Список »). – Eran

+0

Я бы предложил 'new HelloWorld ' вместо 'new HelloWorld ' – Blip

0

В main метода:

for (Type type : new HelloWorld().getTypes()) {//Here also you did not mention the type for the new object since 
} 

Попробуйте это основной метод:

for (Type type : new HelloWorld<Type>().getTypes()) { 

} 
+1

Когда вы объявили 'List ', тогда нет необходимости добавлять 'Type' в' ArrayList' инициализацию , – Blip

+0

Да, конечно. Но в коде он поместил как 'new ArrayList <>()'. Это должно быть либо 'new ArrayList()', либо 'new ArrayList ()'. Не так ли? В противном случае будет ошибка в строке «Несколько маркеров в этой строке \t - Оператор« <> »не разрешен для уровня источника ниже 1.7 - ArrayList - это необработанный тип. Ссылки на общий тип ArrayList должны быть параметризированы \t - Тип безопасности: выражение типа ArrayList требует необработанного преобразования, чтобы соответствовать ' –

+0

_" // здесь вам не нужно упоминать даже тип вообще: 'new ArrayList (); '' _ ... все еще не правильно. В Java 7 новый ArrayList <>() 'отлично подходит и на самом деле является рекомендуемым способом создания экземпляров родовых типов. Посмотрите «оператор алмаза Java 7». –

1

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

Error: Type mismatch: cannot convert from element type Object to HelloWorld.Type

Так как с указанием возвращенного элемент имеет тип Object я решил типовому бросайте его List<Type> как показано ниже:

public static void main(String[] args) { 
    for (Type type : (List<Type>)new HelloWorld().getTypes()) { 

    } 
} 

Это успешно скомпилирован с предупреждением, так Я использовал -Xlint с javac, чтобы увидеть, что это предупреждение, и я нашел следующее:

HelloWorld.java:15: warning: [rawtypes] found raw type: HelloWorld 
     for (Type type : (List<Type>)new HelloWorld().getTypes()) { 
             ^
    missing type arguments for generic class HelloWorld<T> 
    where T is a type-variable: 
    T extends Object declared in class HelloWorld 
HelloWorld.java:15: warning: [unchecked] unchecked cast 
     for (Type type : (List<Type>)new HelloWorld().getTypes()) { 
                  ^
    required: List<Type> 
    found: List 
2 warnings 

Здесь я был поражен, увидев второе предупреждение. В нем указано, что требуется List<Type>, но найдено List a RAW TYPE. Таким образом, это означает, что если вы инициализируете объект необработанного типа и вызываете метод, который возвращает переменную с генериками, эта переменная также будет преобразована в RAW TYPE. Для того, чтобы проверить это я реализовал класс HelloWorldTest как:

public class HelloWorldTest<T>{ 
    private T t; 

    public HelloWorldTest(T t){ 
     this.t = t; 
    } 

    public T getT(){ 
     return t; 
    } 
} 

Затем я изменил код, чтобы проверить состояние как:

public class HelloWorld<T> { 

    private HelloWorldTest<Integer> test = new HelloWorldTest<>(1); 

    public HelloWorldTest<Integer> getTest(){ 
     return test; 
    } 

    public static void main(String[] args) { 
     HelloWorldTest<Integer> hello = new HelloWorld().getTest(); 
    } 
} 

компилируется успешно, но с предупреждениями, так используя -Xlint переключатель для компиляции I получить следующие предупреждения:

HelloWorld.java:10: warning: [rawtypes] found raw type: HelloWorld 
     HelloWorldTest<Integer> hello = new HelloWorld().getTest(); 
              ^
    missing type arguments for generic class HelloWorld<T> 
    where T is a type-variable: 
    T extends Object declared in class HelloWorld 
HelloWorld.java:10: warning: [unchecked] unchecked conversion 
     HelloWorldTest<Integer> hello = new HelloWorld().getTest(); 
                   ^
    required: HelloWorldTest<Integer> 
    found: HelloWorldTest 
2 warnings 

Итак, здесь мы также находим, что HelloWorldTest был преобразован в сырье тип.

Наконец, мы можем заключить, что: Если вы инициализируете объект необработанного типа и вызываете метод, который возвращает переменную с генериками, эта переменная также будет преобразована в RAW TYPE.

Теперь, когда я заменил

HelloWorldTest<Integer> hello = new HelloWorld().getTest(); 

с

Integer hello = new HelloWorld().getTest().getT(); 

Как и ожидалось, я получил ошибку:

HelloWorld.java:10: error: incompatible types: Object cannot be converted to Integer 
     Integer hello = new HelloWorld().getTest().getT(); 
                ^
1 error 

Наконец, если заменить основной метод в моей реализации HelloWorld класса с:

public static void main(String[] args) { 
     String hello = (String) new HelloWorld().getTest().getT(); 
    } 

Он успешно компилируется только предупреждение бытия:

HelloWorld.java:10: warning: [rawtypes] found raw type: HelloWorld 
      String hello = (String) new HelloWorld().getTest().getT(); 
             ^
    missing type arguments for generic class HelloWorld<T> 
    where T is a type-variable: 
    T extends Object declared in class HelloWorld 
1 warning 

Это довольно заблуждение, поскольку это, безусловно, работать на ошибки во время выполнения и снова иллюстрирует опасность RAW TYPE в дженерик.

+0

« Плюс »для хорошего исследования. – MaxZoom

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