2011-04-02 83 views
10

我知道相當多一點如何使用C++ - 模板 - 不是專家,介意你。與Java泛型(和斯卡拉,就此而言),我有我的diffuculties。也許,因爲我試圖將我的C++知識轉化爲Java世界。我在其他地方看過,「他們沒有什麼特別之處:Java泛型只是語法上的糖節省,C++模板只是一個榮耀的預處理器」:-)Java泛型,支持「專業化」?與C++模板的概念相似?

我很確定,兩者都有點簡化了。所以,瞭解大和細微的差別,我嘗試啓動專業化

C++我可以設計一個模板(類的功能),其作用於任何類型T支持我所需的操作:

template<typename T> 
T plus(T a, T b) { return a.add(b); } 

現在這潛在地增加了plus()操作的任何類型的可以add() [注1] [1]

。 0

因此,如果T支持add(T)我的模板工作。如果沒有, 只要我不使用plus(),編譯器就不會抱怨。在Python 我們稱之爲「鴨打字」:*如果它像鴨子,叫起來像鴨子, 它鴨子,這是修改*(當然,使用type_traits了一下, 但只要我們沒有概念,這是如何C++模板工作,對嗎?)

我想,這是怎麼Java中的泛型工作,是不是?泛型類型I設備被用作「模板」,如何操作任何我嘗試在其中放置的東西,對吧?據我的理解,我可以(或必須)將約束放在類型參數上:如果我想在我的模板中使用add,我必須聲明類型參數爲implement Addable。正確?所以,沒有「鴨子打字」(好或壞)。現在

,在C++我可以選擇上一類專門有沒有add()

template<> 
T plus<MyX>(MyX a, MyX b) { return a + b; } 

即使所有其他類型仍然可以使用「默認」的實施,現在我添加了一個特殊的MyX - 沒有運行時間開銷。

是否有任何Java泛型具有相同目的的機制?當然,在編程中一切都是可行的,但我的意思是在概念上,沒有任何技巧和魔法?

回答

8

不,Java中的泛型不能以這種方式工作。

對於泛型,如果沒有泛型,就無法做任何事情 - 只需避免編寫大量的強制類型,編譯器確保所有類型都是類型安全的(只要您沒有收到某些警告或者抑制那些)。

因此,每種類型的變量只能調用它的邊界定義的方法(無鴨打字)。

此外,不存在代碼生成(除了一些適配器方法委託給其他參數類型用於實現通用類型的目的方法)。假設你有這樣的事情

/** 
* interface for objects who allow adding some other objects 
*/ 
interface Addable<T> { 
    /** returns the sum of this object and another object. */ 
    T plus(T summand); 
} 

然後,我們可以創造我們sum方法有兩個參數:

public static <T extends Addable<T>> T sum(T first, T second) { 
    return first.plus(second); 
} 

靜態方法被編譯到相同的字節碼這樣的(在標註的其他類型的信息) :

public static Addable sum(Addable first, Addable second) { 
    return first.plus(second); 
} 

這就是所謂的類型擦除

現在這個方法可以被稱爲對於每對可加成類型的兩個元件,像這樣的:

public class Integer implements Addable<Integer> { 
    public Integer plus(Integer that) { 
     return new Integer(this.value + that.value); 
    } 

    // private implementation details omitted 
} 

什麼這裏發生的是,編譯器會創建一個這樣的附加的合成方法:

public Object plus(Object that) { 
    return this.plus((Integer)that); 
} 

這個方法只能通過具有正確類型的泛型代碼來調用,這保證了編譯器,假設你沒有在某個地方進行一些不安全的轉換 - 那麼這裏拋出的(Integer)會捕獲錯誤(並引發ClassCastException)。

sum方法現在總是調用plus方法的第一個對象,沒有辦法解決這個問題。沒有爲每個類型參數生成的代碼(這個是Java泛型和C++模板之間的關鍵區別),所以我們不能簡單地用生成的方法替換一個生成的方法。

當然,可以創建第二sum方法等提出(超載)irreputable,但如果直接在源代碼中使用MyX類型,而不是當您呼叫從sum方法,這將僅被選擇這恰好其他一些通用的代碼與MYX被參數,就像這樣:

public static <T extends Addable<T>> product (int times, T factor) { 
    T result = factor; 
    while(n > 1) { 
     result = sum(result, factor); 
    } 
    return result; 
} 

現在product(5, new MyX(...))會調用我們sum(T,T)方法(這反過來又調用plus方法),沒有任何重載sum(MyX, MyX)方法。

(JDK 7增加了一個新的dynamic方法分配模式,允許專門通過在運行時每次吵架,但這不是使用Java語言,只打算通過其他基於JVM的語言中使用。)

+2

類型擦除方法應該是'可加總和(可加第一,可加第二)' – irreputable 2011-04-02 13:10:32

+0

@irreputable:謝謝,你說得對。我改變了這一點。 – 2011-04-02 15:11:54

+0

對於'sum'泛型如何工作的很好的解釋。現在我可以將它翻譯成我以前的Java知識。但是這裏擦除的是什麼? 「類型擦除」的名稱是指什麼? – towi 2011-04-03 14:11:43

2

HI,

java泛型與C++模板不同。

例子:

Java代碼:

public <T> T sum(T a, T b) { 
    T newValue = a.sum(b); 
    return newValue; 
} 

在java中的代碼不能工作,因爲仿製藥的基礎是類java.lang.Object,所以你只能使用這個類的方法。

你可以構建這個Methis Hotel酒店是這樣的:

public <T extends Number> T sum(T a, T b) { 
    T newValue = a.sum(b); 
    return newValue; 
} 
在這種情況下,仿製藥的基礎是類java.lang.Number中,所以你可以使用整數,雙,龍ECC

..

方法「sum」依賴於java.lang.Number的實現。

再見

3

沒有 - 但你的具體問題是更大的超載問題。

有沒有問題定義2種plus方法,如這些

<T extends Addable> 
T plus(T a, T b) { .. } 

MyX plus(MyX a, MyX b) { .. } 

這個工程即使MyXAddable;的javac知道第二plus比1 plus更具體的,所以當你調用plus有兩個MyX指定參數時,選擇第二plus。在一定意義上的Java確實允許「專」版本的方法:

f(T1, T2, .. Tn) 

f(S1, S2, .. Sn) 

的偉大工程,如果每個SiTi

對於泛型類的子類型,我們可以做

class C<T extends Number> { ... } 

class C_Integer extends C<Integer>{ ... } 

調用者必須使用C_Integer而不是C<Integer>來選擇「專業」版本。


關於鴨子打字:Java在靜態打字中更加嚴格 - 除非它是鴨子,它不是鴨子。

+0

這只是重載,如果''加'方法是使用'T'從一些泛型方法調用並用'MyX'調用的,則它不起作用。 – 2011-04-02 14:00:30