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++中,擁有不同引數型別的例項是不同的型別。