什麼是泛型?為什麼要使用泛型?
我們在編寫程式時,經常遇到兩個模組的功能非常相似,只是一個是處理int型別資料,另一個處理String型別資料,或者其
他自定義型別資料,但是我們沒有辦法,只能分別寫多個方法處理每種資料型別,因為方法的引數型別不同。那有一種方法,
在方法中傳入通用的資料型別,就可以用來合併程式碼,這就是泛型。
為什麼要使用泛型
為了瞭解這個問題,我們先看下面的程式碼,程式碼省略了一些內容,但功能是實現一個棧,這個棧只能出來int資料型別:
public class Stack
{
private int[] m_item;
public int pop(){…}
public void Push(int item){…}
public Stack(int i)
{
this.m_item = new int[i];
}
}
上面的程式碼執行的很好,但是,當我們需要一個棧來儲存String型別時,該怎麼辦呢?很多人就想把上面的程式碼複製一份,
再把int改成String不就行了。當然,這樣做本身是沒有任何問題的,但一個優秀的程式猿是不會這麼做的,因為他想到
若以後需要long、ode型別的棧該怎麼做呢?還要再複製嗎?優秀的程式猿會想到用一個通用的型別Object來實現這個棧。
public class Stack
{
private object[] m_item;
public object Pop(){…};
public void Push(object item){…}
public Stack(int i)
{
this.m_item = new[i];
}
}
這個棧寫的不錯,它非常靈活,可以接收任何資料型別,可以說是一勞永逸。但全面的講,也不是沒有缺陷的,主要表現在:
當Stack處理值型別時,會出現裝箱、拆箱操作,但將用到的資料型別的強制轉換操作,會增加處理器的負擔。
在資料型別的強制轉換上還有更嚴重的問題(假設stack是Stack的一個例項):
Node1 x = new Node1();
stack.Push(x);
Node2 y = (Node2)stack.Pop();
上面的程式碼在編譯時是完全沒有問題的,但由於push了一個Node1型別的資料,但在Pop時卻要求轉換為Node2型別,這將
出現程式執行時的型別轉換異常,但卻逃離了編譯器的檢查。
針對object型別棧的問題,我們引入泛型,他可以優雅的解決這些問題。泛型用一個通用的資料型別T來代替object,在類
例項化時制定T的型別,執行時(Runtime)自動編譯為原生代碼,執行效率和程式碼質量都有很大提高,並且保證資料型別
安全。
使用泛型
下面是用泛型來重寫上面的棧,用一個通用的資料型別T來作為一個佔位符,等待在例項化時用一個實際的型別來代替。讓
我們來看看泛型的為例;
public class Stack<T>
{
privateT[] m_item;
public T Pop(){…}
public void Push(T item){…}
public Stack(int i){
this.m_item = new T[i];
}
}
類的寫法不變,只是引入了通用資料型別T就可以使用與任何資料型別,並且型別安全。這個類的呼叫方法
//例項化只能儲存int型別的類
Stack<int> a = new Stack<int>(100);
a.Push(10);
a.Push("8888");//這行編譯不通過,因為類A只接收int型別的資料
int x = a.Pop();
Stack<String> b = new Stack<String>(100);
b.Push(10);//這行編譯不通過,因為類b只接收String型別的資料
String y = b.Pop();
這個類和Object實現的類有截然不同的區別:
1.它是型別安全的。例項化了int型別的棧,就不能處理Stirng型別的資料,其他的資料型別也一樣。
2.無需裝箱和拆箱。這個類在例項化時,按照所傳入的資料型別生成原生代碼,原生代碼資料型別已確定,
所以無需拆箱和裝箱。
3.無需資料轉換。