2013-06-24 9 views
8

Date un'occhiata a questo semplice esempio di generici Java:L'elenco <T> non è uguale a Lista <T>?

class List<T> { 
    T head; 
    List<T> next; 
} 

class A<T> { 
    List<T> l; 

    public <T> int length() { 
     List<T> l = this.l; 
     int c = 1; 
     while (l.next != null) { 
      c++; 
      l = l.next; 
     } 
     return c; 
    } 

    public static void main(String[] args) { 
     A<Integer> a = new A<Integer>(); 
     a.l = new List<Integer>(); 
     a.l.head = 123; 
     a.l.next = new List<Integer>(); 
     a.l.next.head = 432; 
     System.out.println("list length: " + a.length()); 
    } 
} 

ottiene una compilazione errore, sostenendo che i tipi sono incompatibili, ma sostenendo che le due variabili sono dello stesso tipo:

$ javac A.java && java A 
A.java:10: incompatible types 
found : List<T> 
required: List<T> 
     List<T> l = this.l; 
         ^
1 error 

Se cambio la prima riga di lunghezza() a List<T> l = (List<T>)(Object)this.l;, funziona. Perché?

+1

Creare una classe il cui nome è in conflitto con una classe JDK comune ('java.util.List' in questo caso) è una cattiva idea. La confusione abbonderà. – yshavit

risposta

19

Hai dichiarato un metodo generico all'interno di una classe generica con questa linea:

public <T> int length() { 

Questo <T> è diverso da quello del <T> vostra classe. Secondo la JLS Section 6.3:

L'ambito di parametro del tipo di una classe (§8.1.2) è la sezione dei parametri tipo della dichiarazione di classe, la sezione dei parametri di tipo qualsiasi superclasse o superinterfaccia della dichiarazione di classe, e il corpo della classe .

Non è necessario ri-dichiarare <T> sul metodo; il parametro di tipo della classe è già in ambito.

Per utilizzare parametro di tipo generico della vostra classe <T>, avere il vostro metodo non dichiarare un altro <T> e semplicemente utilizzare il vostro classe <T>:

public int length() { 

Per comprendere il motivo per cui il vostro pezzo fuso funziona:

List<T> l = (List<T>)(Object)this.l; 

Puoi gettare qualsiasi oggetto su Object. Quindi stai trasmettendo il risultato a List<T>. Puoi sempre lanciarlo in qualsiasi cosa tu voglia; Java eseguirà semplicemente un ClassCastException in fase di runtime se non fosse realmente un List in fase di esecuzione. Ma il compilatore avverte anche che utilizza operazioni non controllate o non sicure, perché non può garantire che questo <T> sia l'originale <T>.

Per illustrare la differenza tra <T> s, è possibile utilizzare <U> come parametro di tipo generico per il metodo e ottenere gli stessi risultati:

public <U> int length() { 
    List<U> l = (List<U>)(Object)this.l; 

Questo compila con lo stesso avviso di sicurezza tipo.

Se si è certi che la sicurezza del tipo può essere garantita, è possibile utilizzare @SuppressWarnings("unchecked") per annotare il metodo. Ma qui, vorrei ancora rimuovere completamente il parametro generico di tipo dal metodo e usare il parametro type della classe.

Problemi correlati