今天看了幾篇文章深有體會,可以說把以前工作中一些情況串起來了
泛型:就是一種不確定的數據類型。
// 比如:ArrayList<E> E就是泛型。 這種不確定的數據類型需要在使用這個類的時候才能夠確定出來。
// 泛型可以省略,如果省略,默認泛型是Object類型。
// 泛型的好處:
// 1. 省略了強轉的代碼。
// 2. 可以把運行時的問題提前到編譯時期。
為什么要使用泛型?
為了了解這個問題,我們先看下面的代碼,代碼省略了一些內容,但功能是實現一個棧,這個棧只能處理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、Node類型的棧該怎么做呢?還要再復制嗎?優秀的程序員會想到用一個通用的數據類型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>
{
private T[] 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類型的棧,就不能處理String類型的數據,其他的數據類型也一樣。
2.無需裝箱和拆箱。這個類在實例化時,按照所傳入的數據類型生成本地代碼,本地代碼數據類型已確定,所以無需裝箱和拆箱。
3.無需類型轉換。