泛型
泛型的定义及目的
泛型的定义主要有以下两种:
在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
不论使用那个定义,泛型的参数在真正使用泛型时都必须作出指明。
一些强类型程序语言支持泛型,其主要目的是加强类型安全及减少类转换的次数,但一些支持泛型的程序语言只能达到部分目的。
伪代码例子
類 例泛類 { 值 : T 設置值(新值 : T) { 值 := 新值 } 獲取值() : T { 返回 值 } } 例方法1() { 例物件 : 例泛類 例物件 := 新 例泛類() 例物件.設置值(5) 输出整数(例对象.获取值()) } 例方法2() { 例物件 : 例泛類 例物件 := 新 例泛類() 例物件.設置值(5.5) 输出浮点数(例对象.获取值()) }
在这例子中,例泛类是一个泛型,而T是一个类型参数。在例泛类中没指明T的实际类型,只有例方法1()和例方法2()在使用例泛类时才加以指明。
运行这例子的例方法1()将输出整数5,而运行例方法2()将输出浮点数5.5。
一些程序语言的泛型特性
.NET Framework的泛型
.NET Framework泛型的参数只可以代表类,不能代表个别对象。由于.NET Framework泛型的类型参数之实际类型在运行时均不会被消除,运行速度会因为类型转换的次数减少而加快。另外,使用GetType()方法可于程序运行时得知泛型及其类型参数的实际类型,更可以运用反射编程。
.NET Framework允许对个别泛型的类型参数进行约束,包括以下几种形式(假设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 等实现相似功能。
#includeclassB{...};classD:publicB{...};templatevoidSFINAE(conststd::enable_if_t<std::is_base_of::value,T>&t);templatevoidSTATIC_ASSERT(constT&t){static_assert(std::is_pod::value,"Use with POD types only!");}
如上所示,std::enable_if(std::enable_if_t 是 std::enable_if::type 的缩写)利用 SFINAE 规则来实现模板类型参数约束的手段之一。其实现方式是若布尔判断为假,则把类型设为 void,而这将导致 const void & 这种不合法的类型出现,从而禁止这种类型参数的使用。
static_assert 则在布尔判断失败时把后面的字符串作为消息内容报告为编译错误。
在编译时,每个被使用的封闭泛型类型(即是所有泛型参数的实际类型都已被指明的泛型)都会有独立的编码产生,编译程序会在此时确保类型安全性。可是如果泛型要运用其泛型参数的某成员,而该泛型参数又不包含该成员的时候,编译程序所产生的错误信息或会看似与实际问题无关,增加除错的难度。
免责声明:以上内容版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。感谢每一位辛勤著写的作者,感谢每一位的分享。
- 有价值
- 一般般
- 没价值