2010-03-02 4 views
15

Ho studiato e sperimentato con Java Generics per un po ', ma mi sono imbattuto in qualcosa che non posso spiegare. Prendiamo ad esempio il seguente codice:Classi interne tipizzate generiche in Java

public class Question { 
    public <T> Sub<T> getSub(Class<T> c) { 
     return new Sub<T>(c); 
    } 
    public class Sub<S> { 
     private Class<S> c; 
     public Sub(Class<S> c) { 
      this.c = c; 
     } 
     public void add(S s) { 
     } 
    } 
} 

E il codice di prova:

import generics.Question.Sub; 

public class Answer { 
    public static void main(String [] args) { 
     Question q = new Question(); 
     Sub<String> s = q.getSub(String.class); 
     s.add(""); 
    } 
} 

Quando questo viene eseguito dà un messaggio di errore meravigliosamente criptico:

C:\Answer.java:8: incompatible types 
found : generics.Question.Sub<java.lang.String> 
required: generics.Question.Sub<java.lang.String> 
     Sub<String> s = q.getSub(String.class); 
1 error 

Ora, attraverso alcuni esperimenti Ho capito come prevenire l'errore del compilatore. Posso rendere la classe Sub una classe interna statica oppure ho bisogno di fare riferimento alla classe Sub come Question.Sub <String>. Quello che non posso fare è spiegare perché ho bisogno di farlo.

Ho letto qualche parte della documentazione Java su Generics ma nessuno copre questo caso particolare.

Qualcuno può spiegare perché il codice è un tipo incompatibile nella sua forma attuale?

operativa -Editazione-

Guardando a questo più mi rendo conto che ho lo stesso comportamento al di fuori di Netbeans. Se ho il codice nella seguente struttura:

generics\ 
generics\Question.java 
generics\Answer.java 

Quando compilo i file insieme, non ottengo l'errore:

C:\>javac generics\Question.java generics\Answer.java 

C:\> 

Tuttavia, quando compilo Domanda e poi risposta, io ottenere l'errore:

C:\>javac generics\Question.java 

C:\>javac generics\Answer.java 
generics\Answer.java:8: incompatible types 
found : generics.Question.Sub<java.lang.String> 
required: generics.Question.Sub<java.lang.String> 
     Sub<String> s = q.getSub(String.class); 
           ^
1 error 

Ho sentito qualcosa menzionato su Type Erasure. È questo il caso in questa situazione?

+0

Questo codice funziona per me in Eclipse. Quale IDE/compilatore stai usando? – polygenelubricants

+0

Posso anche compilarlo senza problemi usando la versione java "1.6.0_15" – Steen

+0

Netbeans 6.7.1 con JDK 1.5.0_14. Se lo compilo al di fuori di Netbeans sono d'accordo, compila bene. Investigherò ulteriormente. Grazie per il feedback. – gencoreoperative

risposta

1

La cancellazione dei caratteri è una proprietà del modo in cui i generici sono attualmente implementati in Java. Ciò significa che il tipo di variabili è noto solo al momento della compilazione, ma non in fase di runtime. Così, per esempio, nel seguente:

Map<String,String> map = new HashMap<String,String>(); 

poi il compilatore sa per controllare contro gli elementi messi in in una base stringa/stringa. Tuttavia, il codice compilato non sa nulla del String, String - è ancora possibile inserire gli oggetti con il tipo sbagliato, ad esempio:

Map other = (Map)map; 
other.put(new Integer(3), new Double(4.5); 

Il problema è che il codice compilato non verifica per i tipi di argomenti quando si passa, e nemmeno il runtime (dal momento che le informazioni sul tipo sono state cancellate, quindi, digitare cancellazioni).

Dubito che la cancellazione dei tipi sia un problema qui - poiché al momento della compilazione, si hanno le informazioni complete sul tipo - ma piuttosto che è probabilmente un bug. Ci sono alcuni problemi pelosi con i generici (da un'implementazione) e ci sono diversi compilatori in uso con JavaC ed Eclipse, quindi potrebbero presentare diversi bug. In alcuni casi, il compilatore Eclipse è stato più fedele alle specifiche rispetto al compilatore Sun (quindi Eclipse crea errori mentre Sun non lo fa) ed è principalmente dovuto alla complessità del modo in cui funziona il sistema di tipi.

Quindi è molto probabile uno (o più) bug con generici nel compilatore 1.5.0_14 ...

+0

Posso confermare questo è il caso, vedere di seguito: C: \> javac -version javac 1.6.0_18 C: \> javac src \ generici \ Question.java C: \> javac src \ generici \ risposta. java -cp src Funziona bene. Grazie! – gencoreoperative

+0

In realtà "Ciò significa che il tipo di variabili sono conosciute solo in fase di compilazione, ma non in fase di esecuzione.": Le informazioni sul tipo sono disponibili in fase di esecuzione e sono disponibili mediante reflection. Nei file di classe, i tipi generici sono ancora conservati (in quale altro modo verrà compilato rispetto a una classe esterna con generici), quindi "cancellazione tipo" non sta cancellando i tipi dalla classe, solo dal runtime. – Pindatjuh

+0

Per essere chiari; le informazioni sui tipi generici sono memorizzate in annotazioni sulla classe, ma non sui tipi del metodo. Quindi qualsiasi chiamata non generica può ancora essere utilizzata con esso; tuttavia, le annotazioni vengono utilizzate solo dal compilatore in fase di compilazione. Quindi non fanno realmente parte del runtime, anche se è possibile analizzare il file di classe in fase di runtime. – AlBlue