2009-03-19 29 views
27

In questo esempio semplificato ho una classe generica e un metodo che restituisce una mappa indipendentemente dal parametro type. Perché il compilatore cancella i tipi sulla mappa quando non si specifica un tipo nella classe contenitore?Perché questo codice Java generico non viene compilato?

import java.util.Map; 

public class MyClass<T> 
{ 
    public Map<String, String> getMap() 
    { 
     return null; 
    } 

    public void test() 
    { 
     MyClass<Object> success = new MyClass<Object>(); 
     String s = success.getMap().get(""); 

     MyClass unchecked = new MyClass(); 
     Map<String, String> map = unchecked.getMap(); // Unchecked warning, why? 
     String s2 = map.get(""); 

     MyClass fail = new MyClass(); 
     String s3 = fail.getMap().get(""); // Compiler error, why? 
    } 
} 

Viene visualizzato questo errore del compilatore.

MyClass.java:20: incompatible types 
found : java.lang.Object 
required: java.lang.String 
       String s3 = fail.getMap().get(""); // Compiler error 
+0

Qual è il testo esatto dell'avviso non controllato? Avviso – Powerlord

+0

: [deselezionato] conversione deselezionata –

risposta

31

Capito. Questo in realtà non è un errore, strano come potrebbe sembrare.

Da section 4.8 (raw types) of the JLS:

Il tipo di un costruttore (§8.8), metodo di istanza (§8.8, §9.4), o campo non statico (§8.3) M di un tipo grezzo C che non viene ereditato dai suoi superclassi o superinterfacce è il cancellazione di questo tipo nel generico corrispondente dichiarazione C. il tipo di un membro statico di tipo grezzo C è lo stesso come il tipo nella generico dichiarazione corrispondente a C.

Così, anche se il tipo la firma del metodo non utilizza alcun parametro di tipo della classe stessa, calci la cancellazione di tipo a e la firma diventa effettivamente

public Map getMap() 

In altre parole, credo puoi immaginare un tipo non elaborato come la stessa API del tipo generico ma con tutti i bit <X> rimossi da ovunque (nell'API, non nell'implementazione).

EDIT: Questo codice:

MyClass unchecked = new MyClass(); 
Map<String, String> map = unchecked.getMap(); // Unchecked warning, why? 
String s2 = map.get(""); 

compila perché c'è una conversione implicita ma incontrollato dal Map tipo grezzo Map<String, String>. È possibile ottenere lo stesso effetto, facendo una conversione esplicita (che non fa nulla in fase di esecuzione), in quest'ultimo caso:

// Compiles, but with an unchecked warning 
String x = ((Map<String, String>)fail.getMap()).get(""); 
+1

È stupido. Grazie Jon! –

+2

Hm ... ha un senso ... ma è incredibilmente stupido allo stesso tempo ... –

+2

Non è così stupido secondo me. – alexmeia

3

Hm ... sfortunatamente non posso dirti perché fallisce. Ma posso offrirti una soluzione semplice:

Modificare il tipo di fail in MyClass<?>, quindi verrà compilato correttamente.

+0

Un cast funziona anche bene. Perché un tipo di? influenzare il tipo di mappa? Non ha ancora senso per me. –

+1

@Motlin: Jon lo ha spiegato abbastanza bene: MyClass non è un tipo non elaborato. –

+0

Ora che capisco perché la tua soluzione funziona, la userò invece di un cast. Grazie. –

1

tipi generici vengono cancellati dopo la compilazione.

Quando si esegue:

Map<String, String> map = unchecked.getMap(); 

il gioco è costringere un cast da Map per Mappa < String, String>, e questo è il motivo per cui l'avviso incontrollato. Tuttavia, dopo che si può fare:

String s2 = map.get(""); 

perché mappa è di tipo Map < String, String>.

Tuttavia, quando si esegue

String s3 = fail.getMap().get(""); 

non trasmetti contenuti fail.getMap() per qualsiasi cosa, quindi è considerato come chiaramente Mappa, Mappa Non < String, String>.

Che cosa si dovrebbe fare in quest'ultimo è qualcosa di simile:

String s3 = ((Map<String, String>fail.getMap()).get(""); 

che sarà comunque gettato un avvertimento, ma funzionerà comunque.

+2

Allora perché compila il primo? "Cancellazione" non è la risposta completa qui. –

+0

Sì, hai ragione. Immagino che la risposta di Jon sia la migliore qui. – Seb

2

Domanda molto interessante e risposta molto interessante di Jon Skeet.

Voglio solo aggiungere qualcosa sulla stupidità o non stupidità di questo comportamento del compilatore java.

Penso che il compilatore presuma che se non si specifica il parametro type in una classe generc non si è in grado (o non si vuole) utilizzare alcun parametro di tipo a tutti. Potresti usare una versione di java precedente a 5 o amare per fare cast manualmente.

Non mi sembra così stupido.

Problemi correlati