0%

Template and Generics -- Language Part

眾所週知,Java跟C#都是從C++演變而來。
既然C++有Template,Java在5.0,C#在2.0的時期分別引進了Generics
雖然看起來很像,看其中差異還是不小,列出重要的幾點份來討論。

Generics不能接受Nontype Parameter

由於實作方式的不同,Generics拿掉了這個

lang: cpp
1
2
3
template <typename T, int MAXSIZE> class Stack {
T stack[MAXSIZE];
};

Generic不支援Specialization

不管是Full Specialization或是 Partial Specialization,通通不行。所以也寫不出這樣的程式碼。

lang: cpp
1
2
template <typename T> class Foo {};
template <> class Foo<int> {};

Template不對Type作任何限制,而Generics能擁有Bound Type

這點才是最大的不同,假設我們要寫一個兩樹相加的Generic function。
在C++我們會這麼寫

lang: cpp
1
2
3
template <typename T> T sum2(T a, T b) { return a + b; }
sum2(100, 200);
sum2(string("Hello"), string("World"));

可以看到,只要有定義operator+,我們什麼東西都能相加。
同樣的Code,如果改寫成C#的話

lang: c#
1
2
3
4
public static T sum2<T>(T a, T b)
{
return a + b;
}

連編譯都邊不過。原因出在Template是在Compile-Time完成的,在編譯的時刻就能檢查出operator+是否存在,而Java跟C#的Generics是在Runtime則否,必須對Type有其一定的限制,在這個範例裡面,我們需要這個Type要有相加的能力,才能作相加的動作。

lang: java
1
2
3
4
public <T extends Number> T sum(T a, T b) {
T newValue = a.sum(b);
return newValue;
}

所有從Number衍生出來的子類別,Integer,Double等都可以使用,不過String不行。
不過.Net 4.0之後有更好的解法,掠過編譯時期的型別檢查,不過Java沒有此特性。

lang: c#
1
2
3
4
5
6
public static T sum2<T>(T a, T b)
{
dynamic a_ = a;
dynamic b_ = b;
return a_ + b_;
}

Template不支援Covariance和Contravariance

這是另一個大題目,同樣在.Net 4.0之後,支援了Covariance和Contravariance。Java的版本有無支援也需要確認。
以後有時間再仔細描述。在C#可以寫這樣的Code

Covariance Example lang: c#
1
2
3
4
5
6
7
8
9
10
11
class Base
{
}
class Derived : Base
{
}
public static void Run(IEnumerable<Base> bases)
{
}
IEnumerable<Derived> derivedBases = new Derived[] { new Derived(), new Derived() };
Run(derivedBases);

類似的程式碼在C++就失敗了…

Covariance Example lang: c#
1
2
3
4
5
6
7
8
9
10
11
12
class Animal
{
public void Feed() {}
}
class Frog : Animal {
}
static void DoSomethingToAFrog(Action<Frog> action, Frog frog)
{
action(frog);
}
Action<Animal> feed = animal => { animal.Feed(); };
DoSomethingToAFrog(feed, new Frog());

其他Template高級技巧

例如SFINAETemplate Template Parameter等,由於學習門檻相當高,所以Generics都把它拿掉了。