2012-03-20 55 views
5

我的問題可以概括式的這個片段:泛型和鑄造到正確的類型

public interface TheClass<T> { 
    public void theMethod(T obj); 
} 

public class A { 
    private TheClass<?> instance; 

    public A(TheClass<?> instance) { 
     this.instance = instance; 
    } 

    public void doWork(Object target) { 
     instance.theMethod(target); // Won't compile! 

     // However, I know that the target can be passed to the 
     // method safely because its type matches. 
    } 
} 

A類使用的TheClass一個實例,其泛型類型不明。它具有一個方法,目標傳遞爲Object,因爲TheClass實例可以用任何類進行參數化。但是,編譯器不會讓我像這樣傳遞目標,這是正常的。

我該怎麼辦才能繞過這個問題?

髒的解決方案是聲明實例爲TheClass<? super Object>,它工作正常,但在語義上是錯誤的...

另一種解決方案我使用之前是聲明實例爲原料的類型,只是TheClass,但它是壞的練習,所以我想糾正我的錯誤。

public class A { 
    private TheClass<Object> instance; // type enforced here 

    public A(TheClass<?> instance) { 
     this.instance = (TheClass<Object>) instance; // cast works fine 
    } 

    public void doWork(Object target) { 
     instance.theMethod(target); 
    } 
} 
+2

你爲什麼不還鍵入'A'? – 2012-03-20 10:15:45

+0

+1,一個很好的問題!等待答案...... – aProgrammer 2012-03-20 10:17:38

+0

A是我製作的開源庫的一部分,許多人都使用它,而打字A意味着強大的api突破。最近我發現了這個問題,因爲之前我使用原始類型爲「TheClass」實例,因爲我不知道這是一個糟糕的概念。它工作得很好,但被認爲是不好的做法,所以我想糾正這種誤用。 – 2012-03-20 10:19:06

回答

4
public class A { 
    private TheClass<Object> instance; 

    public A(TheClass<Object> instance) { 
     this.instance = instance; 
    } 

    public void do(Object target) { 
     instance.theMethod(target); 
    } 
} 

public class A<T> { 
    private TheClass<T> instance; 

    public A(TheClass<T> instance) { 
     this.instance = instance; 
    } 

    public void do(T target) { 
     instance.theMethod(target); 
    } 
} 
+0

+1,他們的代碼仍然會像以前一樣工作。這正是我正在輸入的內容;但你先到了那裏! – 2012-03-20 10:21:35

+0

您的第一個想法將我發送給解決方案,謝謝!實際上,我在實例字段的聲明中強制實現了Object類型,就像你一樣。不過,我仍然在構造函數參數中允許使用通配符,但將此實例強制轉換爲強制類型。這隻會在編譯時產生影響,並且不會從我的用戶請求任何內容(請參閱上次編輯的問題)。 – 2012-03-20 10:31:06

1

解決的辦法是也鍵入A。使用通配符?會使您丟失TheClass的類型信息,並且以後無法恢復。有一些醜陋的黑客,你可以做,但你最好的拍攝是也是鍵入A

public interface TheClass<T> { 
    public void theMethod(T obj); 
} 

public class A<T> { 
    private TheClass<T> instance; 

    public A(TheClass<T> instance) { 
     this.instance = instance; 
    } 

    public void doIt(T target) { 
     instance.theMethod(target); 
    } 
} 

它不會破壞任何API要麼。

+0

+1,謝謝。輸入A對我來說不是一種可能性,因爲它會給圖書館增加無用的複雜性,但是你是對的,那是唯一的解決方案之一。 – 2012-03-20 10:32:24

1

原因的編譯錯誤是?通配符表示在Java中未知類型。您可能會在聲明一個具有未知通用參數的變量,但不能實例化其中的一個。這意味着在您的構造函數中傳入的泛型類可能已經被創建爲保存與您稍後嘗試使用的內容不兼容的類型。例如:

public class A { 
    public static void main(String[] args) { 
     TheClass<String> stringHolder = null; // should constrain parameters to strings 
     A a = new A(stringHolder); 
     a.donot(Float.valueOf(13)) ; // this is an example of what could happen 
    } 

    private TheClass<?> instance; 

    public A(TheClass<?> instance) { 
     this.instance = instance; 
    } 

    public void do(Object target) { 
     instance.theMethod(target); 
    } 
} 

在這種情況下,編譯器會阻止您編寫容易出錯的代碼。正如其他人指出的那樣,您應該爲您的A類添加一個通用參數類型,以限制允許的類型 - 這將消除編譯時錯誤。

一些建議閱讀:Oracle Generics Trail

+0

謝謝。我在過去幾天深入細緻地學習了泛型理論來解決這個問題,所以我理解它的機制。如果我在使用原始類型之前知道理論,我就不會遇到這種情況。現在完成了,我試圖刪除原始類型,但遇到了這個問題。 – 2012-03-20 10:47:05

+0

無論如何,你的代碼在運行時會失敗,拋出異常並停止程序。沒關係,這就像一個隱含的斷言,錯誤會向用戶顯示他們的代碼有錯誤。這不會是一個編譯時錯誤,但它仍然是一個錯誤,並且不會有未定義的行爲,只是使用調試堆棧跟蹤新鮮而乾淨的崩潰:)對於您的示例,它將幫助人們理解爲什麼編譯時實時驗證很重要。 – 2012-03-20 10:49:05

+0

@AurélienRibon - 我發佈的代碼僅用於說明目的。我確實將'do'方法更改爲'donot',但是就我糾正錯​​誤而言。 – Perception 2012-03-20 10:53:22