[CareerCup] 14.4 Templates Java模板

Grandyang發表於2015-11-14

 

14.4 Explain the difference between templates in C++ and generics in Java.

 

在Java中,泛式程式設計Generic Programming的實現是通過一種就做型別擦除Type Erasure的機制來實現的。當原始碼轉為Java虛擬機器JVM的位元組程式碼時擦除引數的型別,例如下面的例子:

Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);

在編譯過程中,被重寫為:

Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);

 

這跟C++中有很大的不同。在C++中,模板是一個巨集設定Macro Set,編譯器對每一個型別的模板程式碼都建立一份拷貝。驗證這一點可以通過以下事實: MyClass<Foo>的一個例項不會跟MyClass<Bar>共享一個靜態變數,但是兩個MyClass<Foo>之間會共享一個靜態變數,參見如下程式碼:

template<class T>
class MyClass {
public:
    static int val;
    MyClass(int v) { val = v; }
};

template<typename T>
int MyClass<T>::val;

class Foo;
class Bar;

int main() {

    MyClass<Foo> *foo1 = new MyClass<Foo>(10);
    MyClass<Foo> *foo2 = new MyClass<Foo>(15);
    MyClass<Bar> *bar1 = new MyClass<Bar>(20);
    MyClass<Bar> *bar2 = new MyClass<Bar>(35);
    
    cout << foo1->val << endl; // will equal 15
    cout << foo2->val << endl; // will equal 15
    cout << bar1->val << endl; // will equal 35
    cout << bar2->val << endl; // will equal 35
    
    return 0;
}

 

而在Java中,靜態變數會在所有的MyClass的例項中共享,不論其引數是否相同,參見下列程式碼:

public class Foo {}
public class Bar {}

public static class MyClass<T> {
    public static int val;
    public MyClass(int v) { val = v; }
}

public static void main (String[] args) {
    System.out.println("Hello World!");
    MyClass<Foo> foo1 = new MyClass<Foo>(10);
    MyClass<Foo> foo2 = new MyClass<Foo>(15);
    MyClass<Bar> bar1 = new MyClass<Bar>(20);
    MyClass<Bar> bar2 = new MyClass<Bar>(35);
    
    System.out.println(foo1.val); // will equal 35
    System.out.println(foo2.val); // will equal 35
    System.out.println(bar1.val); // will equal 35
    System.out.println(bar2.val); // will equal 35
}

 

由於架構的不同,Java的泛式程式設計和C++的模板還有許多不同:

- C++可以使用主要型別,例如int,而Java只能使用Integer

- Java中,你可以限制模板的引數型別為一個確定的型別。例如,你可能使用泛式程式設計來實現一個CardDeck,限定其引數型別必須從CardGame派生過來。

- C++中的引數型別可以被例項化,而Java中的不行。

- 在Java中,引數型別(例如MyClass<Foo>中的Foo)不能用於靜態方法或變數,因為這些會被MyClass<Foo>和MyClass<Bar>所共享。而C++中這些類是不同的,所以可以用於靜態方法或變數中。

- 在Java中,MyClass的所有例項,不管其引數型別是什麼,都是同一個型別,其引數型別在執行時被擦除了。而C++中,擁有不同引數型別的例項是不同的型別。

 

相關文章