泛型編程

泛型程序設計（英文：generic programming）是程序設計語言的一種風格或范型。泛型允許程式設計師在強類型程序設計語言中編寫代碼時使用一些以後才指定的類型，在實例化時作為參數指明這些類型。各種程序語言和其編譯器、運行環境對泛型的支持均不同。Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Visual Basic .NET 稱之為泛型（generics）；ML、Scala 和 Haskell 稱之為參數多態（Parametric polymorphism）；C++ 和 D稱之為模板。具有廣泛影響的1994年版的《Design Patterns》一書稱之為參數化類型（parameterized type）。

## 泛型的定義及目的

泛型的定義主要有以下兩種：

- 在程序編碼中一些包含類型參數的類型，也就是說泛型的參數只可以代表類別，不能代表個別物件。（這是當今常見的定義）
- 在程序編碼中一些包含參數的類別。其參數可以代表類別或物件等等。（現在人們大多把這稱作模板）

不論使用哪個定義，泛型的參數在真正使用泛型時都必須作出指明。

一些強類型程序語言支持泛型，其主要目的是加強類型安全及減少類別轉換的次數，但一些支持泛型的程序語言只能達到部份目的。

## 偽代碼例子

```text
類　例泛類<T> {
　　值 : T

　　設置值(新值 : T) {
　　　　值 := 新值
　　}

　　獲取值() : T {
　　　　返回　值
　　}
}

例方法1() {
　　例物件 : 例泛類<整數型>
　　例物件 := 新　例泛類<整數型>()
　　例物件.設置值(5)
　　輸出整數(例物件.獲取值())
}

例方法2() {
　　例物件 : 例泛類<浮點數型>
　　例物件 := 新　例泛類<浮點數型>()
　　例物件.設置值(5.5)
　　輸出浮點數(例物件.獲取值())
}
```

在這例子中，`例泛類`是一個泛型，而`T`是一個類型參數。在`例泛類`中沒指明`T`的實際類型，只有`例方法1()`和`例方法2()`在使用`例泛類`時才加以指明。

運行這例子的`例方法1()`將輸出整數5，而運行`例方法2()`將輸出浮點數5.5。

## 一些程序語言的泛型特性

### .NET 的泛型

.NET 泛型的參數只可以代表類別，不能代表個別物件。由於 .NET 泛型的類型參數之實際類型在運行時均不會被消除，運行速度會因為類型轉換的次數減少而加快。另外，使用`GetType()`方法可於程序運行時得知泛型及其類型參數的實際類型，更可以運用反射式編程。

```c#
using System;

// 定義一個泛型列表類，T 表示類型參數
public class GenericList<T>
{
    private T[] _items; // 存儲列表items的數組
    private int _count; // 列表中items的計數

    // 構造函數，初始化列表的容量
    public GenericList(int capacity)
    {
        _items = new T[capacity];
        _count = 0;
    }

    // 添加item到列表中
    public void Add(T item)
    {
        if (_count < _items.Length)
        {
            _items[_count] = item;
            _count++;
        }
        else
        {
            Console.WriteLine("List capacity reached."); // 列表容量已滿
        }
    }

    // 根據索引獲取列表中的item
    public T GetItem(int index)
    {
        if (index >= 0 && index < _count)
        {
            return _items[index];
        }
        else
        {
            throw new IndexOutOfRangeException("Index out of range."); // 索引超出範圍
        }
    }
}

public class Program
{
    public static void Main()
    {
        // 創建一個存儲整數的泛型列表
        GenericList<int> intList = new GenericList<int>(3);
        intList.Add(1);
        intList.Add(2);
        intList.Add(3);
        Console.WriteLine(intList.GetItem(1));  // 輸出: 2

        // 創建一個存儲字符串的泛型列表
        GenericList<string> stringList = new GenericList<string>(2);
        stringList.Add("Hello");
        stringList.Add("World");
        Console.WriteLine(stringList.GetItem(0));  // 輸出: Hello
    }
}
```

在上面的例子中展示了一個簡單的泛型列表類 `GenericList<T>`，它可以存儲任何類型的數據（由 `T` 指定）。`Program` 類中的 `Main` 方法演示了如何使用這個泛型類來存儲和檢索整數和字串。

.NET 允許對個別泛型的類型參數進行約束，包括以下幾種形式（假設`T`是泛型的類型參數，`C`是一般類別、泛類，或是泛型的類型參數）：

- `T`是一個類別。
- `T`是一個值類型。
- `T`具有無參數的公有建構方法。
- `T`實現界面`I`。
- `T`是`C`，或繼承自`C`。

### Java 的泛型

Java 泛型的參數只可以代表類別，不能代表個別物件。由於Java泛型的類型參數之實際類型在編譯時會被消除，所以無法在運行時得知其類型參數的類型，而且無法直接使用基本值類型作為泛型類型參數。Java編譯程序在編譯泛型時會自動加入類型轉換的編碼，故運行速度不會因為使用泛型而加快。

由於運行時會消除泛型的對象實例類型資訊等缺陷經常被人詬病，Java及JVM的開發方面也嘗試解決這個問題，例如：Java通過在生成字節碼時添加類型推導輔助資訊，從而可以通過反射接口獲得部分泛型資訊；通過改進泛型在JVM的實現，使其支持基本值類型泛型和直接獲得泛型資訊等。

Java允許對個別泛型的類型參數進行約束，包括以下兩種形式（假設`T`是泛型的類型參數，`C`是一般類別、泛類，或是泛型的類型參數）：

- `T`實現接口`I`。
- `T`是`C`，或繼承自`C`。

### C++的泛型（模板）

C++ 泛型的參數可以代表類別或個別物件。在一般意義上，C++ 缺乏對泛型的類型參數進行直接約束的手段，但可利用 SFINAE（模板代換失敗非錯誤，指在模板實例化過程中的錯誤僅意味此次代換失敗，並不一定產生編譯錯誤）規則及 C++11 的 static_assert 等實現相似功能。

```c++
#include <type_traits>

class B{
...
};
class D: public B{
...
};
template<typename T>
void SFINAE(const std::enable_if_t<std::is_base_of<B, T>::value, T> &t);
template<typename T>
void STATIC_ASSERT(const T &t){
    static_assert(std::is_pod<T>::value, "Use with POD types only!");
}
```

如上所示，std::enable_if（std::enable_if_t 是 std::enable_if::type 的縮寫）利用 SFINAE 規則來實現模板類型參數約束的手段之一。其實現方式是若布爾判斷為假，則把類型設為 void，而這將導致 const void & 這種不合法的類型出現，從而禁止這種類型參數的使用。

static_assert 則在布爾判斷失敗時把後面的字串作為消息內容報告為編譯錯誤。

在編譯時，每個被使用的封閉泛型類型（即是所有泛型參數的實際類型都已被指明的泛型）都會有獨立的編碼產生，編譯程序會在此時確保類型安全性。可是如果泛型要運用其泛型參數的某成員，而該泛型參數又不包含該成員的時候，編譯程序所產生的錯誤信息或會看似與實際問題無關，增加除錯的難度。

## 外部連結

- generic-programming.org
- Alexander A. Stepanov, Collected Papers of Alexander A. Stepanov (creator of the STL)

C++/D

- Walter Bright, Templates Revisited .
- David Vandevoorde, Nicolai M Josuttis, C++ Templates: The Complete Guide, 2003 Addison-Wesley.

C#/.NET

- Jason Clark, " Introducing Generics in the Microsoft CLR ," September 2003, MSDN Magazine, Microsoft.
- Jason Clark, " More on Generics in the Microsoft CLR ," October 2003, MSDN Magazine, Microsoft.
- M. Aamir Maniar, Generics.Net . An open source generics library for C#.

Delphi/Object Pascal

- Nick Hodges, " Delphi 2009 Reviewers Guide ," October 2008, Embarcadero Developer Network, Embarcadero.
- Craig Stuntz, " Delphi 2009 Generics and Type Constraints," October 2008
- Dr. Bob, " Delphi 2009 Generics "
- Free Pascal: Free Pascal Reference guide Chapter 8: Generics , Michaël Van Canneyt, 2007
- Delphi for Win32: Generics with Delphi 2009 Win32 , Sébastien DOERAENE, 2008
- Delphi for .NET: Delphi Generics , Felix COLIBRI, 2008

Eiffel

- Eiffel ISO/ECMA specification document

Haskell

- Johan Jeuring, Sean Leather, José Pedro Magalhães, and Alexey Rodriguez Yakushev. Libraries for Generic Programming in Haskell. Utrecht University.
- Dæv Clarke, Johan Jeuring and Andres Löh, The Generic Haskell user's guide
- Ralf Hinze, " Generics for the Masses ," In Proceedings of the ACM SIGPLAN International Conference on Functional Programming (ICFP), 2004.
- Simon Peyton Jones, editor, The Haskell 98 Language Report , Revised 2002.
- Ralf Lämmel and Simon Peyton Jones, "Scrap Your Boilerplate: A Practical Design Pattern for Generic Programming," In Proceedings of the ACM SIGPLAN International Workshop on Types in Language Design and Implementation (TLDI'03), 2003. (Also see the website devoted to this research)
- Andres Löh, Exploring Generic Haskell, Ph.D. thesis, 2004 Utrecht University.
- Generic Haskell: a language for generic programming

Java

- Gilad Bracha, Generics in the Java Programming Language , 2004.
- Maurice Naftalin and Philip Wadler, Java Generics and Collections, 2006, O'Reilly Media, Inc.
- Peter Sestoft, Java Precisely, Second Edition, 2005 MIT Press.
- language/generics.html, 2004 Sun Microsystems, Inc.
- Angelika Langer, Java Generics FAQs