不可变类型

最简单的复合类型是不可变类型。不可变类型的实例(如元组 )是值。它们的字段在创建后无法更改。在许多方面,不可变类型就像 Tuple,它具有类型本身和每个字段的名称。

单例类型

根据定义,复合类型包含许多更简单的类型。在朱莉娅,这个数字可以为零; 也就是说,允许不可变类型包含任何字段。这与空元组 () 相当。

为什么这有用呢?这种不可变类型被称为单例类型,因为它们中只有一个例子可能存在。这些类型的值称为单值。标准库 Base 包含许多这样的单例类型。这是一个简短的清单:

  • Voidnothing 的类型。我们可以验证 Void.instance(这是检索单例类型的单例值的特殊语法)确实是 nothing
  • 任何媒体类型,例如 MIME"text/plain",都是具有单个实例 MIME("text/plain") 的单例类型。
  • Irrational{:π}Irrational{:e}Irrational{:φ} 和类似类型是单例类型,它们的单例实例是无理值π = 3.1415926535897... 等。
  • 迭代器大小特征 Base.HasLengthBase.HasShapeBase.IsInfiniteBase.SizeUnknown 都是单例类型。

Version >= 0.5.0

  • 在 0.5 及更高版本中,每个函数都是单例类型的单例实例! 像任何其他单例值一样,我们可以从 typeof(sin).instance 恢复函数 sin

由于它们不包含任何内容,因此单例类型非常轻量级,并且编译器可以经常对它们进行优化,以避免运行时开销。因此,它们非常适合于特征,特殊标记值以及人们想要专注的功能。

要定义单例类型,

julia> immutable MySingleton end

要为单例类型定义自定义打印,

julia> Base.show(io::IO, ::MySingleton) = print(io, "sing")

要访问单例实例,

julia> MySingleton.instance
MySingleton()

通常,人们会将此指定为常数:

julia> const sing = MySingleton.instance
MySingleton()

包装类型

如果零字段不可变类型是有趣且有用的,那么也许单字段不可变类型甚至更有用。这些类型通常称为包装器类型,因为它们包装一些底层数据,为所述数据提供备用接口。Base 中包装类型的一个例子是 String 。我们将定义一个类似于 String 的类型,命名为 MyString。这种类型将由字节的向量(一维数组 )支持(UInt8)。

首先,类型定义本身和一些自定义显示:

immutable MyString <: AbstractString
    data::Vector{UInt8}
end

function Base.show(io::IO, s::MyString)
    print(io, "MyString: ")
    write(io, s.data)
    return
end

现在我们的 MyString 型已经可以使用了! 我们可以为它提供一些原始的 UTF-8 数据,并按我们喜欢的方式显示:

julia> MyString([0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21])
MyString: Hello, World!

显然,这种字符串类型在变得像 Base.String 类型一样可用之前需要做很多工作。

真正的复合类型

也许最常见的是,许多不可变类型包含多个字段。一个例子是标准库 Rational{T} 类型,它包含两个 fieds:分子的 num 字段和分母的 den 字段。模拟这种类型的设计相当简单:

immutable MyRational{T}
    num::T
    den::T
    MyRational(n, d) = (g = gcd(n, d); new(n÷g, d÷g))
end
MyRational{T}(n::T, d::T) = MyRational{T}(n, d)

我们已成功实现了一个简化有理数的构造函数:

julia> MyRational(10, 6)
MyRational{Int64}(5,3)