1. 程式人生 > >什麼是泛型?為什麼要使用泛型?

什麼是泛型?為什麼要使用泛型?

我們在編寫程式時,經常遇到兩個模組的功能非常相似,只是一個是處理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.無需資料轉換。