泛型编程

时间:2023-12-22 13:18  

多态
特设多态
  • 函数重载
  • 运算符重载
参数多态
  • 泛化函数
  • 泛型编程
子类型
  • 虚函数
  • 单一与动态分派
  • 多分派
  • 双分派
编程范型
  • 指令式
    • 过程式
    • 块结构
    • 异常处理
    • 结构化
    • 模块化
  • 函数式
    • 头等函数
    • 纯函数式
    • 隐式
    • 模式匹配
    • 推导式
  • 递归
  • 阵列式
  • 面向对象
    • 基于类
    • 基于原型
    • 契约式
    • 面向方面
    • 面向代理
  • 多态
    • 运算符重载
    • 泛型
    • 多分派
  • 元编程
    • 模板
    • 反射式
      • 实化
      • 同像性
      • 元对象
      • 元类
    • 面向特性
    • 面向语言
  • 数据流程
    • 同步式
    • 响应式
    • 流处理
    • 基于流程
  • 宣告式
    • 逻辑式
      • 回答集
    • 约束式
    • 数据查询
    • 框架本体
  • 并发模型
    • 协程
    • 演员模型
    • 通信顺序进程
  • 并行模型
    • 分叉会合
    • 整体同步
    • 映射归约
  • 面向堆栈
  • 非确定性
  • 数据驱动
  • 事件驱动
  • 自动机
  • 可微分
  • 概率式

泛型程序设计英文: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)。

泛型的定义及目的

泛型的定义主要有以下两种:

  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今常见的定义)
  2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)

不论使用哪个定义,泛型的参数在真正使用泛型时都必须作出指明。

一些强类型程序语言支持泛型,其主要目的是加强类型安全及减少類转换的次数,但一些支持泛型的程序语言只能达到部分目的。

伪代码例子

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

在这例子中,例泛类是一个泛型,而T是一个类型参数。在例泛类中没指明T的实际类型,只有例方法1()例方法2()在使用例泛类时才加以指明。

运行这例子的例方法1()将输出整数5,而运行例方法2()将输出浮点数5.5。

一些程序语言的泛型特性

.NET 的泛型

.NET 泛型的参数只可以代表类,不能代表个别对象。由于 .NET 泛型的类型参数之实际类型在运行时均不会被消除,运行速度会因为类型转换的次数减少而加快。另外,使用GetType()方法可于程序运行时得知泛型及其类型参数的实际类型,更可以运用反射式编程。

.NET 允许对个别泛型的类型参数进行约束,包括以下几种形式(假设T是泛型的类型参数,C是一般类、泛类,或是泛型的类型参数):

  • T是一个类。
  • T是一个值类型。
  • T具有无参数的公有建构方法。
  • T实现接口I
  • TC,或继承自C

Java 的泛型

Java 泛型的参数只可以代表类,不能代表个别对象。由于Java泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型,而且无法直接使用基本值类型作为泛型类型参数。Java编译程序在编译泛型时会自动加入类型转换的编码,故运行速度不会因为使用泛型而加快。

由于运行时会消除泛型的对象实例类型信息等缺陷经常被人诟病,Java及JVM的开发方面也尝试解决这个问题,例如:Java通过在生成字节码时添加类型推导辅助信息,从而可以通过反射接口获得部分泛型信息;通过改进泛型在JVM的实现,使其支持基本值类型泛型和直接获得泛型信息等。

Java允许对个别泛型的类型参数进行约束,包括以下两种形式(假设T是泛型的类型参数,C是一般类、泛类,或是泛型的类型参数):

  • T实现接口I
  • TC,或继承自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 则在布尔判断失败时把后面的字符串作为消息内容报告为编译错误。

在编译时,每个被使用的封闭泛型类型(即是所有泛型参数的实际类型都已被指明的泛型)都会有独立的编码产生,编译程序会在此时确保类型安全性。可是如果泛型要运用其泛型参数的某成员,而该泛型参数又不包含该成员的时候,编译程序所产生的错误信息或会看似与实际问题无关,增加调试的难度。

数据源

  1. ^ Standard ECMA-335 Common Language Infrastructure (CLI) 4th Edition. June 2006 [2007-08-03]. (原始内容存档于2013-06-26).  引文格式1维护:冗余文本 (link)
  2. ^ James Gosling, Bill Joy, Guy Steele, Gilad Bracha. The Java Language Specification Third Edition. 2005 [2007-08-03]. (原始内容存档于2009-02-26).  引文格式1维护:冗余文本 (link)

 

本文版权遵从外房网免责声明,更多问题请发送邮件至:
sam@glofang.com

为您推荐:

12小时在线QQ客服
周一至周日 9:00-21:00

广告联系

邮箱:glofang@glofang.com