2015-06-30 26 views
5

ho scritto il seguente codice:Comprendere classi generiche interne

public class Test<T> { 

    public void method(){ 
     B b = new B(); 
    } 

    public class B{ } 
} 

//Some method in some class contains the following lines  
Test<Integer> t = null; 
Test.B b = t.new B(); //warning Test.B is a raw type 

Perché ho ricevuto questo avviso? La declinazione del tipo interno B non contiene il parametro type, quindi non è un tipo generico. Inoltre, the specification ci dà la seguente:

Una classe è generico se si dichiara una o più variabili di tipo

La classe B non dichiarare le variabili di tipo. Allora, perché è un tipo generico?

+0

Questa riga 'Test.B b = t.nuovo B();' compila? Qualcuno con più conoscenza di Java potrebbe spiegare cosa significa "t.new"? Questo mi sembra un errore di sintassi ai miei occhi. – thatidiotguy

+0

@thatidiotguy Questo è un classico classificatore di istanze classe class expr. [Look] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.9) –

+0

@ScottHunter 'Oggetto'. 'B' non eredita da nessuna classe, quindi eredita implicitamente da' Object'. – Turing85

risposta

3

Sono d'accordo sul fatto che le specifiche delle classi generiche non coprano chiaramente il proprio scenario. Ma la specifica di raw types fa:

Più precisamente, un tipo grezzo viene definito come uno di:

  • Il tipo di riferimento che è formata prendendo il nome di una dichiarazione di tipo generico senza lista di argomenti di accompagnamento.

  • Un tipo di matrice il cui tipo di elemento è un tipo non elaborato.

  • A non tipo static membro di un tipo R grezza che non viene ereditato da una superclasse o superinterfaccia di R.

+1

Molto molto interessante! Ma le regole sono piuttosto sfocate ... –

+3

BTW, c'è una nota sotto la citazione che hai fornito: __Il tipo di membro (i) di Inner dipende dal parametro type di Outer. Se Outer è raw, Inner deve essere trattato come raw, in quanto non esiste un binding valido per T .__ Non lo ha detto esplicitamente, che 'Test .B' è generico –

1

quello che stai cercando non funziona perché il tipo di B non è B, ma Test<T>.B

Per ottenere questo lavoro, fanno di classe B statica.

public static class B{ } 
5

Sebbene la classe interna B non dichiara alcuna variabile tipo, un'istanza di esso fa riferimento implicitamente un'istanza della classe esterna, che fa.

Perché ho ricevuto questo avviso?

Test<Integer> t = null; 
Test.B b = t.new B(); //warning Test.B is a raw type 

Perché avete dichiarato la variabile b con un tipo grezzo. Invece, è possibile dichiarare:

Test<Integer>.B b = t.new B(); // no warning! 
+0

Perché ha parametri di tipo che non sono specificati. – celticminstrel

1

B è una classe interna. Non può esistere senza un'istanza di Test. Pertanto, dipende anche da qualsiasi parametro di tipo di Test. Quando compilato, classe B si trasforma in quanto segue:

public class Test$B { 
    public Test$B(Test paramTest) {} 
} 

(Questo è quello che si otterrà se si compila e poi decompilarlo.)

Si può vedere che ci vuole una Test nel suo costruttore .Anche se i parametri di tipo vengono cancellati durante la compilazione, se hai scritto una classe come te, probabilmente gli darai un parametro di tipo da passare a Test.

2

In realtà non si utilizza il tipo generico. Spiegherò su un esempio un po 'più elaborato:

public class Test<T> { 
    public B method(T t) { 
     B b = new B(t); 
     return (b); 
    } 

    public class B { 
     T value; 

     public B(T value) { 
      this.value = value; 
     } 
    } 
} 

Qui si può vedere chiaramente, che B dipende dal parametro generico T, senza essere generici stesso. Come explained by Andy Thomas, un'istanza di B può essere creata solo in coesistenza con un'istanza di Test. Pertanto, B è (indirettamente) generico. Nel vostro esempio dato:

Test<Integer> t = null; 
Test.B b = t.new B(); //warning Test.B is a raw type 

b non specifica un parametro generico, ma t fa. Questo è, perché ricevi l'avvertimento.

Il modo corretto di scrivere questo codice sarebbe:

Test<Integer> t = null; 
Test<Integer>.B b = t.new B(); 

Con questo, B è completamente specificato e la partita tipi.

2

Sebbene B non sia parametrizzato, Test.B b = t.new B(); contiene un riferimento non elaborato a Test, che è parametrizzato. Ho avvertito l'avviso di scomparire quando ho cambiato la linea di avviso a Test<Integer>.B b = t.new B();