2013-06-24 32 views
8

看一看Java泛型的這個簡單的例子:列表<T>不等於列表<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()); 
    } 
} 

它得到一個錯誤的編譯,聲稱該類型是不兼容的,但聲稱這兩個變量的類型相同:

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

如果我將第一行的長度()更改爲List<T> l = (List<T>)(Object)this.l;,它將起作用。爲什麼?

+1

創建一個名稱與這種常見的JDK類(在這種情況下爲'java.util.List')衝突的類是一個壞主意。混亂將會比比皆是。 – yshavit

回答

19

您已經聲明瞭這條線的通用類中的泛型方法:

public <T> int length() { 

<T>比不同類的<T>。根據JLS Section 6.3

類的類型參數的範圍(§8.1.2)的類聲明的類型參數 部分,任何 超類或超類聲明的類型參數部,和類 正文。

您不需要在您的方法上重新聲明<T>;該類的類型參數已經在範圍內。

要使用類的泛型類型參數<T>,有你的方法不聲明另一個<T>,只需使用你的類的<T>

public int length() { 

要了解爲什麼你的鑄造工作:

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

你可以將任何物體投射到Object。然後你將結果投射到List<T>。你總是可以將它投射到任何你想要的東西上;如果在運行時它不是List,Java在運行時會簡單地拋出ClassCastException。但是編譯器也會給出警告,說它使用未經檢查或不安全的操作,因爲它不能保證這個<T>是原始的<T>

爲了說明差異<T> S之間,你可以使用<U>作爲方法的泛型類型參數,並得到相同的結果:

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

這將編譯與同類型的安全警告。

如果您確實知道類型安全性可以得到保證,您可以使用@SuppressWarnings("unchecked")來標註您的方法。但是在這裏,我仍然會從整個方法中刪除泛型類型參數,並使用類的類型參數。