泛型編程
此條目的語調或風格或許不適合百科全書。 (2020年9月21日) |
此條目可參照英語維基百科相應條目來擴充。 (2020年9月21日) |
泛型程式設計(英文: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)。
泛型的定義及目的
泛型的定義主要有以下兩種:
- 在程式編碼中一些包含類型參數的類型,也就是說泛型的參數只可以代表類別,不能代表個別物件。(這是當今常見的定義)
- 在程式編碼中一些包含參數的類別。其參數可以代表類別或物件等等。(現在人們大多把這稱作模板)
不論使用哪個定義,泛型的參數在真正使用泛型時都必須作出指明。
虛擬碼例子
類 例泛類<T> {
值 : T
設置值(新值 : T) {
值 := 新值
}
獲取值() : T {
返回 值
}
}
例方法1() {
例物件 : 例泛類<整數型>
例物件 := 新 例泛類<整數型>()
例物件.設置值(5)
輸出整數(例物件.獲取值())
}
例方法2() {
例物件 : 例泛類<浮點數型>
例物件 := 新 例泛類<浮點數型>()
例物件.設置值(5.5)
輸出浮點數(例物件.獲取值())
}
在這例子中,例泛類
是一個泛型,而T
是一個類型參數。在例泛類
中沒指明T
的實際類型,只有例方法1()
和例方法2()
在使用例泛類
時才加以指明。
運行這例子的例方法1()
將輸出整數5,而運行例方法2()
將輸出浮點數5.5。
一些程式語言的泛型特性
.NET 的泛型
.NET 泛型的參數只可以代表类别,不能代表個別物件。由於 .NET 泛型的類型參數之實際類型在執行時均不會被消除,執行速度會因為類型轉換的次數減少而加快。另外,使用GetType()
方法可於程式執行時得知泛型及其類型參數的實際類型,更可以運用反射式編程。
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 允許對個別泛型的類型參數進行約束,包括以下幾種形式[1](假設T
是泛型的類型參數,C
是一般類別、泛類,或是泛型的類型參數):
T
是一個類別。T
是一個值類型。T
具有無參數的公有建構方法。T
實現界面I
。T
是C
,或繼承自C
。
Java 的泛型
Java 泛型的參數只可以代表类别,不能代表個別物件。由於Java泛型的類型參數之實際類型在編譯時會被消除,所以無法在執行時得知其類型參數的類型,而且無法直接使用基本值類型作為泛型類型參數。Java編譯程式在編譯泛型時會自動加入類型轉換的編碼,故執行速度不會因為使用泛型而加快。
由於執行時會消除泛型的對象實例類型資訊等缺陷經常被人詬病,Java及JVM的開發方面也嘗試解決這個問題,例如:Java通過在生成位元組碼時添加類型推導輔助資訊,從而可以通過反射介面獲得部分泛型資訊;通過改進泛型在JVM的實現,使其支援基本值類型泛型和直接獲得泛型資訊等。
Java允許對個別泛型的類型參數進行約束,包括以下兩種形式[2](假設T
是泛型的類型參數,C
是一般類別、泛類,或是泛型的類型參數):
T
實現介面I
。T
是C
,或繼承自C
。
C++的泛型(模板)
C++ 泛型的參數可以代表类别或個別物件。在一般意義上,C++ 缺乏對泛型的類型參數進行直接約束的手段,但可利用 SFINAE(模板代換失敗非錯誤,指在模板實例化過程中的錯誤僅意味此次代換失敗,並不一定產生編譯錯誤)規則及 C++11 的 static_assert 等實現相似功能。
#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<boolean, Type> 是 std::enable_if<boolean, Type>::type 的縮寫)利用 SFINAE 規則來實現模板類型參數約束的手段之一。其實現方式是若布林判斷為假,則把類型設為 void,而這將導致 const void & 這種不合法的類型出現,從而禁止這種類型參數的使用。
static_assert 則在布林判斷失敗時把後面的字串作為訊息內容報告為編譯錯誤。
在編譯時,每個被使用的封閉泛型類型(即是所有泛型參數的實際類型都已被指明的泛型)都會有獨立的編碼產生,編譯程式會在此時確保類型安全性。可是如果泛型要運用其泛型參數的某成員,而該泛型參數又不包含該成員的時候,編譯程式所產生的錯誤資訊或會看似與實際問題無關,增加除錯的難度。
資料來源
- ^ Standard ECMA-335 Common Language Infrastructure (CLI) 4th Edition. June 2006 [2007-08-03]. (原始內容存檔於2013-06-26).
- ^ James Gosling, Bill Joy, Guy Steele, Gilad Bracha. The Java Language Specification Third Edition. 2005 [2007-08-03]. (原始內容存檔於2009-02-26).
參考文獻
- Musser, D. R.; Stepanov, A. A. Generic programming. P. Gianni (編). Symbolic and Algebraic Computation: International symposium ISSAC 1988. Lecture Notes in Computer Science 358. 1989: 13–25. ISBN 978-3-540-51084-0. doi:10.1007/3-540-51084-2_2.
- Stroustrup, Bjarne. Evolving a language in and for the real world: C++ 1991-2006 (PDF). ACM HOPL 2007. 2007 [2018-04-04]. (原始內容存檔 (PDF)於2007-11-20).
- Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. 1994. ISBN 0-201-63361-2.
延伸閱讀
- Gabriel Dos Reis and Jaakko Järvi, What is Generic Programming?, LCSD 2005.
- Gibbons, Jeremy. Backhouse, R.; Gibbons, J.; Hinze, R.; Jeuring, J. , 編. Datatype-generic programming. Spring School on Datatype-Generic Programming 2006. Lecture Notes in Computer Science 4719. Heidelberg: Springer: 1–71. 2007. CiteSeerX 10.1.1.159.1228 .
- Bertrand Meyer. "Genericity vs Inheritance (頁面存檔備份,存於網際網路檔案館)." In OOPSLA (First ACM Conference on Object-Oriented Programming Systems, Languages and Applications), Portland (Oregon), 29 September–2 October 1986, pages 391–405.
外部連結
- 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. ISBN 0-201-73484-2
- 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
- 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. ISBN 90-393-3765-9
- 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. ISBN 0-596-52775-6
- Peter Sestoft, Java Precisely, Second Edition, 2005 MIT Press. ISBN 0-262-69325-9
- Java SE 7, 2004 Sun Microsystems, Inc.
- Angelika Langer, Java Generics FAQs (頁面存檔備份,存於網際網路檔案館)