2013-08-09 2 views
6

Я нашел какой-то странный код, где я сказал: «Это никогда не вызывается, потому что он будет бросать класс cast Exception». Ну, код вызывается и работает.Java generic method возвращает другой тип - исключение

Может кто-нибудь объяснить мне: Почему это работает?

Метод getZipList() определяется как возвращающий список строк, но внутренняя логика возвращает список с разными объектами. Также внутри основного метода список строк ожидается как «список», но в списке есть что-то другое.

public class GenericMethodList{ 
    public static void main(String [] args) 
    { 
     GenericMethodList o = new GenericMethodList(); 
     List<String> list = o.getZipList(true); 

     Iterator<?> iter = list.iterator(); 
     while (iter.hasNext()){ 
      ZipCode zipplace = (ZipCode) iter.next(); 
      System.out.println(zipplace.value); 
     } 
    } 

    public List<String> getZipList(boolean someParameter){ 
     //why is this not throwing an exception at runtime? 
     List list; 
     if(someParameter){ 
      list = getZipCodes();//List<ZipCode> 
     } else { 
      list = getZipStreets();//List<ZipStreet> 
     } 
     return list; 
    } 

    private List<ZipCode> getZipCodes(){ 
     List<ZipCode> list = new ArrayList<ZipCode>(); 
     list.add(new ZipCode("code1")); 
     list.add(new ZipCode("code1")); 
     return list; 
    } 

    private List<ZipStreet> getZipStreets(){ 
     List<ZipStreet> list = new ArrayList<ZipStreet>(); 
     list.add(new ZipStreet("street1")); 
     list.add(new ZipStreet("street2")); 
     return list; 
    } 

    public class ZipCode{ 
     public String value; 
     public ZipCode(String value){ 
      this.value = value; 
     } 
    } 

    public class ZipStreet { 
     public String value; 
     public ZipStreet(String value){ 
      this.value = value; 
     } 
    } 
} 

Благодарим вас за разъяснения.

ответ

4

Вы должны получать предупреждение о компиляторе «непроверенного броска» для строки return list, потому что вы возвращаете необработанный тип в качестве параметризованного типа. В этом положении «неконтролируемый отбор» выполняется из необработанного типа (для которого просто нет информации о типе элементов) в параметризованный тип, объявленный для этого метода. Параметром типа может быть любой произвольный тип —, компилятор просто не знает, что разрешить и что предотвратить.

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

+0

Спасибо за ваш ответ (все остальные). Я не знал, что компилятор удаляет это (Type Erasure): [http://docs.oracle.com/javase/tutorial/java/generics/erasure.html] Но тогда для меня ясно, почему это работает. И да, я получил предупреждение о компиляторе :-) – Phil

0

Потому что вы используете «тип» типа «сырой» тип (т. Е. Общий тип без заданного параметра типа). Сырые типов существует для старых целей, но следует избегать в новом коде, потому что она теряет типобезопасность, как вы уже видели:

http://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html

0

Вы должны получить предупреждение. Из-за стирания типа (файл .class фактически не перечисляет параметризованный тип для обратной совместимости), тип времени выполнения List<E> равен List, а JVM не видит никакой разницы. Ошибка появится в отдаленном месте, когда кто-то попытается вытащить String из этого List и вместо этого получит ZipCode.

0

Вы должны пройти под вращением во время компиляции.

Note: Some input files use unchecked or unsafe operations. 
Note: Recompile with -Xlint:unchecked for details. 

Поскольку Дженерики основаны на реализации type erasure. Таким образом, во время работы у него нет информации о универсальном типе (общая информация называется Non-Reifiable Types)

0

Проблема в том, что вы объединяете в свое решение RawType с общим. Вы выполняете это в методе getZipList(boolean). Поскольку List нет ни одного типа, вы тормозите тип гарантии. Следующее место, где вы обманываете компилятор, - это то, как вы объявляете Iterator<?>, так как вы не объявляете общий параметр, я буду хранить объект. Поэтому в следующий раз вы избегаете уверенности в том, что вы используете преимущества, чтобы посмотреть, куда вы отбрасываете ожидаемый тип. Вот почему его работа. Обычно кастинг выполняется компилятором, но вы правильно его реализуете сами.

Если ваш код будет выглядеть следующим образом

Iterator<String> iter = list.iterator(); 

while (iter.hasNext()){ 
    ZipCode zipplace = (ZipCode) iter.next();// Compilation error 
    System.out.println(zipplace.value); 
    } 

Если ваш основной метод будет выглядеть следующим образом:

while(String s : list) { 
System.out.wirteln(s); 
} 

ClassCastException будет отброшен. Потому что код «будет выглядеть»

Iterator<Object> iter = list.iterator(); 
    while (iter.hasNext()){ 
     ZipCode zipplace = (String) iter.next(); 
     System.out.println(zipplace.value); 
    } 

Как и в Java, вы не можете использовать этот способ. Исключение выбрано.

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