2015-04-23 16 views
13

Ho seguente codice:intesa anomalia sicurezza rispetto ai tipi con i generici 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)); 
} 

Questo programma funziona bene e la stampa kshitiz. Fallisce solo se sostituisco l'ultima riga con:

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

Eccezione:

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

Cosa sta succedendo qui?

+3

Specificare la versione Java – m0skit0

+0

Quale eccezione viene generata? – McCroskey

+0

'Eccezione nel thread" main "java.lang.ClassCastException: java.lang.String non può essere lanciato su java.lang.Integer' – DavidS

risposta

12

Immagino che tu sappia che i tipi generici sono andati in fase di esecuzione. Ora, per vedere cosa succede dietro le quinte, diamo un'occhiata a questo pezzo di codice

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()); 
} 

e l'uscita del 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 

Così si vede che in seconda convocazione, la stringa viene cast in un intero, mentre nel primo caso viene considerato come un oggetto. Finché la stringa viene gestita come qualsiasi altro oggetto, tutto va bene, ma non appena si chiama un metodo del tipo di elemento della lista, l'oggetto viene convertito in quel tipo.

+1

È interessante il motivo per cui il compilatore ha inserito questo cast non necessario. – talex

0

I generici in Java sono danneggiati dalla progettazione.

Nel tuo List<Integer> i = new ArrayList(s); hai un ArrayList crudo. Significa che ignori il parametro type e funziona come se fosse un Object. Quindi (implicitamente) trasmetti un ArrayList a List<Integer>, ma i generici non vengono controllati in fase di runtime, quindi la JVM non si preoccupa della mancata corrispondenza.

Quando si esegue System.out.println(i.get(0)) non si converte String in Integer perché viene invocato il metodo 'println (Object) `.

In questo System.out.println(i.get(0).getClass()) freni magiche perché per qualche motivo (probabilmente si può trovare questo motivo in specifiche, ma vi consiglio di non farlo) il vostro String GETS pressofuso a Integer.

+0

Grazie per la modifica. – talex

+3

Puoi discutere se è meglio se il codice fallisce all'assegnazione di 'ArrayList' a' ArrayList 'o, come al solito, quando chiama' get', tuttavia, non vedo abbastanza differenze da giustificare la chiamata al comportamento corrente " broken by design ". Dopo tutto, il risultato è lo stesso, ignorare un avviso in fase di compilazione porta a un'eccezione di runtime ... – Holger