泛型编程

泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。AdaDelphiEiffelJavaC#F#SwiftVisual Basic .NET 称之为泛型(generics);MLScalaHaskell 称之为参数多态(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 允許對個別泛型的類型參數進行約束,包括以下幾種形式[1](假設T是泛型的類型參數,C是一般、泛類,或是泛型的類型參數):

  • T是一個。
  • T是一個值類型。
  • T具有無參數的公有建構方法。
  • T实现I
  • TC,或繼承自C

Java 的泛型

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

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

Java允許對個別泛型的類型參數進行約束,包括以下兩種形式[2](假設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. 4th Edition. June 2006.
  2. James Gosling, Bill Joy, Guy Steele, Gilad Bracha. Third Edition. 2005.

參考文獻

  • Musser, D. R.; Stepanov, A. A. . P. Gianni (编). . 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. (PDF). ACM HOPL 2007. 2007.
  • Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John. . 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. , 编. . Spring School on Datatype-Generic Programming 2006. Lecture Notes in Computer Science. Heidelberg: Springer: 1–71. 2007. 已忽略未知参数|citeseerx= (帮助)
  • 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.

外部連結

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
Delphi/Object Pascal
Eiffel
Haskell
Java
This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.