2015-05-02 11 views
16

Si consideri il seguente codice:Perché il metodo di chiamata con ritorno generico su una classe generica è considerato non sicuro da javac?

public class Main { 
    public static class NormalClass { 
     public Class<Integer> method() { 
      return Integer.class; 
     } 
    } 

    public static class GenericClass<T> { 
     public Class<Integer> method() { 
      return Integer.class; 
     } 
    } 

    public static void main(String... args) { 
     NormalClass safeInstance = new NormalClass(); 
     Class<Integer> safeValue = safeInstance.method(); 

     GenericClass unsafeInstance = new GenericClass(); 
     Class<Integer> unsafeValue = unsafeInstance.method(); 
    } 
} 

Se compilo con:

$ javac -Xlint:unchecked Main.java 

Restituisce:

Main.java:16: warning: [unchecked] unchecked conversion 
     Class<Integer> unsafeValue = unsafeInstance.method(); 
                 ^
    required: Class<Integer> 
    found: Class 
1 warning 

Si prega di notare che solo il metodo generico è considerato pericoloso, anche se nessun tipo generico è referenziato sul tipo di reso.

È un errore javac? O c'è una ragione più profonda per questo che non sto prendendo in considerazione?

+0

Non conosco il motivo, ma succede solo se la classe generica in cui è dichiarato il metodo è un tipo non elaborato. – Bubletan

+0

Ho ricevuto l'errore su GenericClass unsafeInstance = new GenericClass(); anziché. Questo perché hai istanziato un tipo generico senza argomenti di tipo. –

+6

Perché quando si sceglie di utilizzare i tipi non elaborati, si sta sostanzialmente dicendo al compilatore che si desidera essere in una modalità legacy non tipicamente. Non usare tipi grezzi. Leggi http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it –

risposta

12

I tipi non elaborati consentivano la compatibilità con il codice scritto prima dell'introduzione dei generici. I tipi non elaborati funzionano semplicemente ignorando tutte le informazioni sul tipo da tutti gli argomenti del metodo e i tipi restituiti, anche digitando informazioni che non sono correlate al parametro type della classe. Questo può portare a risultati strani, come hai trovato. Ma diventa ancora più strano di questo. Ad esempio, questo compila.

public class Main { 

    public static class GenericClass<T> { 
     public void foo(Class<Integer> clazz) { 
     } 
    } 

    public static void main(String... args) { 
     GenericClass unsafeInstance = new GenericClass(); 
     unsafeInstance.foo(String.class); 
    } 
} 
+1

... ma non lo è se si specifica il parametro di tipo generico al momento dell'istanziazione, sebbene ciò non influenzi il metodo 'pippo'. Esempio davvero fantastico! – isnot2bad

2

Al fine di essere compatibile con Java 1.4 del compilatore presuppone che se si lascia cadere gli argomenti generici sulla dichiarazione del tipo di esempio, poi si lavora con la versione speciale della classe dove non esistono affatto generici. E emette un avviso se si mescola un codice Java non generico 1.4 con codice generico Java 1.5+. È più facile che cercare di capire se il tipo generico di restituzione del tuo metodo sia effettivamente indipendente dai parametri. Puoi sempre @SuppressWarning se non ti piace.

3

Il tipo di un costruttore (§8.8), metodo di istanza (§8.4, §9.4), o non statico campo (§8.3) M di tipo C grezza che non viene ereditato dalle sue superclassi o superinterfacce è il tipo di greggio che corrisponde alla cancellazione del suo tipo nella dichiarazione generico corrispondente al C.

Java Language Specification

+0

Anche se ho accettato l'altra risposta, ritengo che anche questa risposta meriti più voti, perché cita la specifica, che chiarisce che si trattava di una decisione progettuale specifica, non di un effetto collaterale non intenzionale dell'implementazione. –

+1

Anche [4.6] (https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.6): * "Il tipo di cancellazione associa anche la firma di un metodo a un firma che non ha tipi parametrizzati. [...] anche il tipo di ritorno di un metodo viene cancellato se la firma del metodo viene cancellata "*. – Radiodef

0

Questo può avere a che fare con l'istanza di GenericClass che è un tipo parametrizzato e quindi digitare argomenti sono necessari in esso. L'avvertimento scompare se facciamo qualcosa di simile al seguente

GenericClass<String> unsafeInstance = new GenericClass<String>(); 
          OR 
GenericClass<?> unsafeInstance = new GenericClass(); 

Secondo il mio punto di vista, il riferimento "unsafeInstance" si riferisce a una classe che è di natura generica al contrario di "safeInstance". In questo modo il compilatore può volere che le informazioni sul tipo siano associate al riferimento prima che qualsiasi metodo venga chiamato utilizzandolo nel codice.

Problemi correlati