6.3 元类型
如果我们试图以上述的抽象类型创建对象,会发现:
julia> Integer(10) 10 julia> typeof(ans) Int64 julia> AbstractFloat(10) 10.0 julia> typeof(ans) Float64
其中,虽然Integer定义整数值10,但实际归属在具体类型Int64下;AbstractFloat的情况同样如此。原因如前所述,抽象类型是无法实例化的,因为其并没有存储结构方面的定义。若要存储这些真实的数值,只能转为其子类型中的某个具体类型(这个过程会由Julia自动执行)。
所谓“元类型”,是具体类型的一种,不但有明确的二进制内存结构,也是构成其他数据的基础。简单来说,元类型是概念单一、结构纯粹的基本类型,其数据在内存中是连续的位序列。
所以在Julia中基于关键字primitive type声明一个元类型时,需要给出明确的位数:
primitive type name bits end primitive type name <: supertype bits end
其中除了告知待声明元类型的名字name外,还需使用bits告知表达该类型时需要的位数,即该类型在容纳数据时的内存结构。
同抽象类型类似,在声明元类型时也可以同时给出其父类型。例如:
primitive type Float16 <: AbstractFloat 16 end
其中,声明了名为Float16的元类型,是浮点型AbstractFloat的一种,用16位表达浮点数。
实际上,第3章介绍的各种整型、浮点型等数字类型都是元类型的一种,都有着对应的元类型声明语句,如下所示:
primitive type Float16 <: AbstractFloat 16 end primitive type Float32 <: AbstractFloat 32 end primitive type Float64 <: AbstractFloat 64 end primitive type Bool <: Integer 8 end primitive type Char <: AbstractChar 32 end primitive type Int8 <: Signed 8 end primitive type UInt8 <: Unsigned 8 end primitive type Int16 <: Signed 16 end primitive type UInt16 <: Unsigned 16 end primitive type Int32 <: Signed 32 end primitive type UInt32 <: Unsigned 32 end primitive type Int64 <: Signed 64 end primitive type UInt64 <: Unsigned 64 end primitive type Int128 <: Signed 128 end primitive type UInt128 <: Unsigned 128 end
其中,Int8、Int16、Int32、Int64及Int128同是Signed的子类型,都是有符号整型;UInt8、UInt16、UInt32、UInt64及UInt128同是Unsigned的子类型,都是无符号整型;而Float16、Float32及Float64则是浮点型AbstractFloat的子类型。
如果将这些元类型及上节介绍的抽象类型之间的继承关系绘制出来,便能够获得更为完整的数值类型拓扑图,如图6-2所示。
需要注意的是,在实际内存操作,一位作为独立的内存操作单元在机器指令中一般是不支持的。所以在定义元类型时,提供的位数值必须是8的倍数,即以字节为单位。上例中的布尔型是Integer的直接子类型,虽然理论上单比特就能够表达一个布尔值,例如1表示真,0表示假,但仍以8位定义了存储结构。
另外,虽然布尔型、Int8类型和UInt8在声明表述上一致,却并不能相互交换或替代,这是因为Julia采用的是主格(Nominative)类型[1]规则,而且它们有着不同的直接父类型:Bool的父类型是Integer,Int8的父类型是Signed,而UInt8的父类型则是Unsigned类型。
图6-2 数字元类型拓扑树
在实践中,如果要查询某个类型的父类型,可使用supertype()函数,例如:
julia> supertype(Signed) Integer julia> supertype(Integer) Real julia> supertype(Real) Number julia> supertype(Number) Any
也可以使用subtypes()获得某个类型的子类型列表,例如:
julia> subtypes(Signed) 5-element Array{Union{DataType, UnionAll},1}: Int128 Int16 Int32 Int64 Int8 julia> subtypes(Real) 4-element Array{Any,1}: AbstractFloat AbstractIrrational Integer Rational
但是,该函数只会列出给定类型的直接后继子类型,不会进行递归查询。
[1] 主格类型系统,即基于名称的类型系统,以声明的类型名称作为相容或等价判定的依据。相对的是结构类型系统,以内部结构作为判定依据,而不是名称。