2013-06-24 18 views
8

Wystarczy popatrzeć na tym prostym przykładzie rodzajowych Java:Lista <T> nie jest równa listy <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()); 
    } 
} 

on dostaje kompilację błędzie, twierdząc, że typy są niezgodne, ale twierdzi, że dwie zmienne są tego samego typu:

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

Jeśli zmienię pierwszy wiersz długości() na List<T> l = (List<T>)(Object)this.l;, to działa. Czemu?

+1

Tworzenie klasy, której nazwa jest w konflikcie z tak powszechną klasą JDK ("java.util.List" w tym przypadku) jest złym pomysłem. Zamieszanie będzie obfitować. – yshavit

Odpowiedz

19

pan uznany sposób ogólny wewnątrz rodzajowe klasy z tej linii:

public <T> int length() { 

Ten <T> jest inna niż klasa na <T>. Według JLS Section 6.3:

Zakres parametru typu klasy w (§8.1.2) jest sekcja parametr typu deklaracji klasy, sekcja parametr typu żadnej nadrzędnej lub superinterface deklaracji klasy, i ciało klasy .

Nie musisz ponownie deklarować <T> dla swojej metody; parametr typu klasy jest już w zakresie.

Aby korzystać z uniwersalnym typem parametru klasie za <T> mają metoda nie deklarują inny <T> i po prostu użyć klasa na <T>:

public int length() { 

Aby zrozumieć dlaczego odlewania działa:

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

Można rzucić dowolny obiekt na numer Object. Następnie rzutujesz wynik na List<T>. Zawsze możesz go rzucić we wszystko, co chcesz; Java po prostu wyrzuci ClassCastException w środowisku wykonawczym, jeśli w rzeczywistości nie była to wersja List. Ale kompilator również ostrzeże, że używa niezaznaczonych lub niebezpiecznych operacji, ponieważ nie może zagwarantować, że ten <T> jest oryginalny <T>.

Aby zilustrować różnicę pomiędzy <T> s, można użyć <U> jako parametr typu rodzajowego do sposobu i uzyskać takie same wyniki:

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

To kompiluje z tego samego ostrzeżenia bezpieczeństwa typu.

Jeśli naprawdę wiesz, że bezpieczeństwo typu może być zagwarantowane, możesz użyć wartości @SuppressWarnings("unchecked") do adnotacji swojej metody. Ale tutaj, chciałbym jeszcze przejść z usunięciem ogólnego rodzaju parametru z metody całkowicie i przy użyciu parametru typu klasy.

Powiązane problemy