在
编程语言理论中,子类型是一种类型多态的形式。这种形式下,子类型可以
替换另一种相关的
数据类型(超类型,英语:supertype)。也就是说,针对超类型元素进行操作的
子程序、函数等程序元素,也可以操作相应的子类型。如果 S 是 T 的子类型,这种子类型
关系通常写作 S <: T,意思是在任何需要使用 T 类型对象的环境中,都可以安全地使用 S 类型的对象。子类型的准确语义取决于具体的
编程语言中“X 环境中,可以安全地使用 Y”的意义。编程语言的
类型系统定义了各自不同的子类型关系。
定义
由于子类型关系的存在,某个对象可能同时属于多种类型,因此,子类型是一种类型多态的形式,也被称作子类型多态(subtype polymorphism)或者包含多态(inclusion polymorphism)。在
面向对象程序设计中,
多态一般仅指这里所说的“子类型多态”,而“
参数多态”则一般被称作泛型程序设计。
子类型与面向对象语言中(类或对象)的
继承是两个概念。子类型反映了类型(即面向对象中的接口)之间的关系;而继承反映了一类对象可以从另一类对象创造出来,是语言特性的实现。因此,子类型也称接口继承;继承称作实现继承。
如果一个类型S是另一个类型T的子类型,则对用T表达式的所有程序P,当用S替换程序P中的T时,程序P的功能不变。
在c++中,以public方式继承的派生类可看做基类的子类型。
性质
某种程度上来说,类型S与类型T
等价。类型S包含T的全部内容,但是类型T不一定包含S的全部内容。对基类所能实施的操作完全适用于派生类(子类型),一个子类型的数据也可以赋值或作为参数传递给类型T的变量。具体可总结为一下几点:
·基类的操作可以实施到派生类对象
·基类指针变量可以指向派生类对象
·派生类对象可以赋值给基类对象
·派生类操作不可以用于基类对象
·派生类指针变量不可以指向基类对象
·基类对象不能赋值给派生类对象
举例
图2中给出了子类型的一个简单实际例子。一般性对象“鸟”(或超类型)引发了三个派生对象(或子类型)“鸭子”、“杜鹃”和“鸵鸟”。每个都以自己的方式改变了基本的“鸟”的概念,但仍继承了很多“鸟”的特征。一个数据对象可以被声名为这四种类型中任何一个。这个图中使用了UML符号,箭头指示方向和超类型和它的子类型之间的联系。
在多数基于类的
面向对象编程语言中,
子类引出子类型:如果A是B的子类,则类A的实例可以用在期望类B的实例的任何上下文中;所以我们称A是B的子类型。一个结论就是声明有类型B的任何变量或形式参数在运行时间可以持有类A的一个值;在这种情况下很多面向对象编程者会声称B是这个变量的“静态类型”而A是它的“动态类型”。这个规则的例外包括
C++语言中的
私有继承(它不创建子类型),和
Eiffel语言中在派生类型上特定运算,在其中继承自基类的特征可以用违反子类型规则的方式去除或修改。
另一个例子是可以允许整数值被用在期望浮点数值的地方,或可以定义包含整数和实数二者的一个类型number的语言。在第一种情况下,整数类型将是浮点数类型的子类型;在第二种情况下,这两个类型都是number的子类型而相互之间无子类型关系。
编程者可利用子类型来以比没有它更抽象的方式来写代码。考虑下面的例子:
如果整数和实数都是number的子类型,则二者任何类型都可以传递给这个函数。为此,子类型经常被认为是一种形式的
多态性。上述例子也可以比较于 C++ 语言的
模板。
在
类型论中,子类型关系经常写为<:,有着A<:B意味着A是B的子类型。在类型论中子类型可用如下事实来特征化,如果A<:B,类型A的任何表达式也可被给予类型B;立法这个特征化的形式类型规则叫做“包容”规则。
方案
类型理论研究者区分两类类型系统:
上面描述的基于类的面向对象子类型描述是名义的;面向对象的结构子类型规则可以声称,如果类型A的一个对象能处理类型B的对象能处理的所有消息(就是说,如果它们定义都同样的
方法),则A是B的子类型,不管二者任何一个是否从继承自其他对象。不是对象类型的类型的健壮的结构子类型规则也是周知的。
带有子类型的编程语言实现可分为两大类:
在面向对象语言中子类型所导致的子类型通常是包含的;联系整数和浮点数的子类型关系,它们有不同表示,通常是强制的。
在定义子类型关系的几乎所有类型系统中,它是自反的(意味着对于任何类型A有A<:A)和传递的(意味着如果A<:B并且B<:C则A<:C)。这得到了在类型上的预序。
记录类型
记录是命名的域(field)的集合。记录类型(types of records)的子类型化包括宽度与深度两种方式。
函数类型
对于函数类型T1→ T2,其子类型为S1→ S2,则T1<: S1且S2<: T2。参数类型S1→ S2为逆变,返回类型为
协变。
允许副作用的语言,如大部分面向对象语言,子类型化还不足以保证安全在另一个上下文中使用。行为子类型化要求保持
不变。
可变引用(mutable reference)的子类型化类似于函数参数与返回值的处理。只写引用是逆变的;只读引用是协变的;可变引用是不变的。
类型强制
在强制子类型化系统(coercive subtyping system),子类型通过从子类型到超类型的隐式
类型转换函数得以定义。对于每个子类型关系 (S<:T),一个强制关系coerce:S→T,使得任何对象s为类型S,可以视作对象coerceS→T(s)具有类型T。类型强制函数可以复合:如果S<:T且T<:U,难么s可以看作类型u在复合强制关系(coerceT→U∘coerceS→T)。类型到其自身的coerceT→T是同一函数idT。