2015-04-23 13 views
13

Mam następujący kod:Zrozumienie typesafety anomalia z rodzajowych Java

public static void main(String[] args) { 
    List<String> s = new ArrayList<String>(); 
    s.add("kshitiz"); 

    //This is not typesafe. It should blow up at runtime 
    List<Integer> i = new ArrayList(s); 
    System.out.println(i.get(0)); 
} 

Ten program działa poprawnie i drukuje kshitiz. To nie tylko jeśli mogę wymienić ostatnią linię:

System.out.println(i.get(0).getClass()); 

Wyjątek:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 

Co się tu dzieje?

+3

Podaj wersję Java – m0skit0

+0

Jakiego wyjątku ona powoduje? – McCroskey

+0

'Wyjątek w wątku" główny "java.lang.ClassCastException: java.lang.String nie może być rzutowany na java.lang.Integer' – DavidS

Odpowiedz

12

Sądzę, że zdajesz sobie sprawę, że typy ogólne zniknęły w czasie wykonywania. Teraz, aby zobaczyć, co się dzieje za kulisami, spójrzmy na ten kawałek kodu

public static Class<?> getClass(final Object object) { 
    return object.getClass(); 
} 

public static void main(final String[] args) { 
    final List<String> s = new ArrayList<String>(); 
    s.add("kshitiz"); 

    // This is not typesafe. It should blow up at runtime 
    final List<Integer> i = new ArrayList(s); 
    System.out.println(getClass(i.get(0))); 
    System.out.println(i.get(0).getClass()); 
} 

i wyjściu javap -c

[...] 
    26: getstatic  #8     // Field java/lang/System.out:Ljava/io/PrintStream; 
    29: aload_2 
    30: iconst_0 
    31: invokeinterface #9, 2   // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 
    36: invokestatic #10     // Method getClass:(Ljava/lang/Object;)Ljava/lang/Class; 
    39: invokevirtual #11     // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
    42: getstatic  #8     // Field java/lang/System.out:Ljava/io/PrintStream; 
    45: aload_2 
    46: iconst_0 
    47: invokeinterface #9, 2   // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 
    52: checkcast  #12     // class java/lang/Integer 
    55: invokevirtual #2     // Method java/lang/Object.getClass:()Ljava/lang/Class; 
    58: invokevirtual #11     // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
    61: return 

Więc widać, że w drugim naborze, ciąg jest rzutuje na liczbę całkowitą, podczas gdy w pierwszym przypadku jest traktowany jako obiekt. Tak długo, jak ciąg jest obsługiwany tak jak każdy inny obiekt, wszystko jest w porządku, ale jak tylko wywołasz metodę typu elementu z listy, obiekt jest rzutowany na ten typ.

+1

To interesujące, dlaczego kompilator umieścił tę niepotrzebną obsadę. – talex

0

Produkty generyczne w języku Java są uszkodzone zgodnie z projektem.

W swoim List<Integer> i = new ArrayList(s); masz surowy ArrayList. Oznacza to, że ignorujesz parametr type i działa on tak, jakby był to Object. Następnie (niejawnie) rzuca się ArrayList do List<Integer>, ale generics nie są sprawdzane w czasie wykonywania, więc JVM nie dba o niedopasowanie.

Po wykonaniu System.out.println(i.get(0)) nie konwertuj String na Integer, ponieważ wywoływana jest metoda "println (Object)".

W tym przypadku magia znika z jakiegoś powodu (prawdopodobnie można znaleźć ten powód w specyfikacjach, ale proponuję, aby nie) Twój String zostanie rzucony do Integer.

+0

Dzięki za edycję. – talex

+3

Możesz omówić, czy lepiej jest, jeśli kod nie powiedzie się przy przypisaniu 'ArrayList' do' ArrayList 'lub, jak to było, podczas wywoływania' get', jednak nie widzę wystarczającej różnicy, aby uzasadnić wywołanie bieżącego zachowania " zepsute według projektu ". W końcu wynik jest taki sam, ignorowanie ostrzeżenia o kompilacji prowadzi do wyjątku środowiska wykonawczego ... – Holger