![]() |
![]() | 此條目可参照英語維基百科相應條目来扩充。(2020年9月21日) 若您熟悉来源语言和主题,请协助参考外语维基百科扩充条目。请勿直接提交机械翻译,也不要翻译不可靠、低品质内容。依版权协议,译文需在编辑摘要注明来源,或于讨论页顶部标记 {{Translated page}} 标签。 |
多态 |
---|
特设多态 |
參數多态 |
子类型 |
泛型程序设计(英文: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 泛型的类型参数之实际类型在运行时均不会被消除,运行速度会因为类型转换的次数减少而加快。另外,使用GetType()
方法可于程序运行时得知泛型及其类型参数的实际类型,更可以运用反射式编程。
usingSystem;// 定義一個泛型列表類,T 表示類型參數publicclassGenericList<T>{privateT[]_items;// 存儲列表items的數組privateint_count;// 列表中items的計數// 構造函數,初始化列表的容量publicGenericList(intcapacity){_items=newT[capacity];_count=0;}// 添加item到列表中publicvoidAdd(Titem){if(_count<_items.Length){_items[_count]=item;_count++;}else{Console.WriteLine("List capacity reached.");// 列表容量已滿}}// 根據索引獲取列表中的itempublicTGetItem(intindex){if(index>=0&&index<_count){return_items[index];}else{thrownewIndexOutOfRangeException("Index out of range.");// 索引超出範圍}}}publicclassProgram{publicstaticvoidMain(){// 創建一個存儲整數的泛型列表GenericList<int>intList=newGenericList<int>(3);intList.Add(1);intList.Add(2);intList.Add(3);Console.WriteLine(intList.GetItem(1));// 輸出: 2// 創建一個存儲字符串的泛型列表GenericList<string>stringList=newGenericList<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及JVM的开发方面也尝试解决这个问题,例如:Java通过在生成字节码时添加类型推导辅助信息,从而可以通过反射接口获得部分泛型信息;通过改进泛型在JVM的实现,使其支持基本值类型泛型和直接获得泛型信息等。
Java允許對個別泛型的類型參數進行約束,包括以下兩種形式[2](假設T
是泛型的類型參數,C
是一般类、泛類,或是泛型的類型參數):
T
实现接口I
。T
是C
,或繼承自C
。C++ 泛型的参数可以代表类或个别对象。在一般意义上,C++ 缺乏对泛型的类型参数进行直接约束的手段,但可利用 SFINAE(模板代换失败非错误,指在模板实例化过程中的错误仅意味此次代换失败,并不一定产生编译错误)规则及C++11 的 static_assert 等实现相似功能。
#include<type_traits>classB{...};classD:publicB{...};template<typenameT>voidSFINAE(conststd::enable_if_t<std::is_base_of<B,T>::value,T>&t);template<typenameT>voidSTATIC_ASSERT(constT&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 则在布尔判断失败时把后面的字符串作为消息内容报告为编译错误。
在编译时,每个被使用的封闭泛型类型(即是所有泛型参数的实际类型都已被指明的泛型)都会有独立的编码产生,编译程序会在此时确保类型安全性。可是如果泛型要运用其泛型参数的某成员,而该泛型参数又不包含该成员的时候,编译程序所产生的错误信息或会看似与实际问题无关,增加除错的难度。