2010-10-21 56 views
22

我想知道在Java中專門化泛型類型的選項有什麼選擇,即在模板化類中有特定類型的特定覆蓋。在我的情況下,我是一個泛型類(類型T)通常返回null,但返回「」(空字符串),當T是字符串類型時,或0(零)時,它的整數類型,等Java泛型(模板)專業化可能(覆蓋具有特定類型的模板類型)

僅僅提供的方法的特定類型的過載產生「方法是不明確的」錯誤:

例如:

public class Hacking { 

    public static void main(String[] args) { 
    Bar<Integer> barInt = new Bar<Integer>(); 
    Bar<String> barString = new Bar<String>(); 

    // OK, returns null 
    System.out.println(barInt.get(new Integer(4))); 

    // ERROR: The method get(String) is ambiguous for the type Bar<String> 
    System.out.println(barString.get(new String("foo"))); 
    } 

    public static class Bar<T> { 

    public T get(T x) { 
     return null; 
    } 

    public String get(String x) { 
     return ""; 
    } 
    } 
} 

是繼承通用類與特定的唯一的選擇類型(請參見以下示例中的StringBar?

public static void main(String[] args) { 
    Bar<Integer> barInt = new Bar<Integer>(); 
    StringBar barString2 = new StringBar(); 

    // OK, returns null 
    System.out.println(barInt.get()); 

    // OK, returns "" 
    System.out.println(barString2.get()); 
    } 

    public static class Bar<T> { 

    public T get() { 
     return null; 
    } 
    } 

    public static class StringBar extends Bar<String> { 
    public String get() { 
     return ""; 
    } 
    } 
} 

這是唯一的方法,我必須爲每個類型創建一個子類,這是我想要專注的,而不是Bar類中get()的重載。

我猜我可以檢查Bar.get()方法中的instanceof,如果(t的instanceof String)返回「」;否則返回「0」。 if(t instanceof Integer)返回0; 否則返回null; }

不過,我已經被教導避免instanceof,並儘可能使用多態。

+0

注意我來自一個C++的世界裏,C++專業化是通過簡單的方法做重載 – AshirusNW 2010-10-21 13:56:58

+2

Java泛型是從C++模板非常不同,因爲泛型是通過擦除實現的。看看這篇文章,它可能會爲你清理很多:http://stackoverflow.com/questions/313584/what-is-the-concept-of-erasure-in-generics-in-java – 2010-10-21 13:59:17

+1

I知道如何實現差異,但我想知道如何克服Java泛型中的缺陷(特別是專用) – AshirusNW 2010-10-21 14:07:16

回答

8

所有事情都考慮到了,共識似乎是問題中提到的StringBar方法方法是唯一的出路。

public static class StringBar extends Bar<String> { 
    public String get() { 
     return ""; 
    } 
    } 
+1

我已經創建並接受了我自己的答案,因爲雖然其他答案有點正確並且達到了這個結論,但它們都含有誤導點或以某種方式誤解了問題。 – AshirusNW 2010-11-05 14:07:39

3

Java中的泛型不是專業化的。他們是爲泛化!如果你想專門針對某些類型,你應該專注於......通過一個子類。

然而,你通常不需要以專門的方式做某件事。你StringBar例子是一種人爲的,因爲你可以有這樣的:

public class Bar<T> { 
    private final T value; 
    public T get() { 
     return value; 
    } 
} 

我不明白爲什麼你需要在這裏專門爲一個字符串。

+0

所以我唯一的選擇是StringBar方法? – AshirusNW 2010-10-21 13:57:20

+0

基本上,get()從其他地方從類中檢索類型爲T的實例,但如果存在錯誤,它將返回null,但如果出現錯誤以防止多餘,我希望它返回字符串「」在字符串專門化的情況下對調用者進行空檢查。 – AshirusNW 2010-10-21 14:03:57

+0

順便說一下,這個例子當然是有人設計的 - 那就是我沒有向你展示真正的來源,這個來源是需要很長的時間才能發佈和減損真正的問題。真正的源代碼有專門化的特定需求。 – AshirusNW 2010-10-21 14:09:46

4

編譯器實際上是正確的,因爲下面的代碼是編譯時檢查(Bar<String> barString = new Bar<String>();)在編譯時,從

public static class Bar<T> { 

    public T get(T x) { 
     return null; 
    } 

    public String get(String x) { 
     return ""; 
    } 
    } 

public static class Bar<String> { 

    public String get(String x) { 
     return null; 
    } 

    public String get(String x) { 
     return ""; 
    } 
    } 

是模棱兩可你不能有2個相同的方法具有相同的返回類型和相同的參數參數。

見喬恩斯基特的解釋:


你也可以繼承Bar<T>創造StringBar(注意我刪除了static關鍵字),並覆蓋get()方法。

public class BarString extends Bar<String> { 

    @Override 
    public String get(String x) { 
     return ""; 
    } 
} 
+2

是啊,我知道爲什麼編譯器會拋出一個錯誤 - 這已經在SO上得到了充分的說明。我想知道的是* do *工作的各種解決方案。我已經提出了子類化(StringBar)方法,我想知道是否有任何其他的專業方法。 – AshirusNW 2010-10-21 14:11:25

+0

我的建議是刪除'public String get(String x)'方法。爲什麼需要如果你有'Bar barString'? – 2010-10-21 14:15:36

+0

barString不能按我喜歡的方式工作。我希望barString.get()返回空字符串,而不是null。 – AshirusNW 2010-10-21 14:39:05

4

在這方面,Java中的泛型與C++中的模板非常不同。不可能像C++那樣編寫一個通用類的特定版本來針對特定情況做不同的事情。在運行時也不可能確定T是什麼 - 這是因爲信息沒有傳遞到字節碼(目標碼)中,所以在運行時甚至不存在。這是由於所謂的「類型擦除」。

BarString和BarInt將是這樣做的顯而易見的方式,但您可以進行改進。例如,您可以編寫一個通用欄來覆蓋常見情況,然後編寫專門的BarString和BarInt來實現特殊情況。確保實例只能通過一個工廠,它接受對象的創建要處理:

class Bar<T> { 
    class BarString extends Bar<String> { 
    // specialist code goes here 
    } 


static Bar<T> createBar(Class<T> clazz) { 
    if (clazz==String.class) { 
    return new BarString(); 
    } else { 
    return new Bar<T>; 
} 

這可能將無法編譯,但我沒有制定出時間確切的語法。它確實說明了原則。