Julia语言程序设计
上QQ阅读APP看书,第一时间看更新

6.5.3 成员访问及不可变性

构造出复合类型的实例对象后,便可通过成员访问符(英文句号)访问其内部成员,形式为:


对象名.字段名

注意 “对象名”与“成员访问符”之间不能有空格。

对于上文中FooA对象的x变量,有:


julia> x.a
1

julia> x.b
2.5

但是如果试图修改其成员的内容,会报错:


julia> x.a = 10
ERROR: type FooA is immutable

julia> x.b = 3.8
ERROR: type FooA is immutable

这是因为FooA在声明时,没有使用mutable进行标识。不过,前文中的FooB类型在声明时,是有mutable标识的。我们创建一个FooB对象,如下:


julia> y = FooB(2, 3.5)
FooB(2, 3.5)

julia> y.a
2

julia> y.b
3.5

再尝试修改其成员值:


julia> y.a = 2.9                  # 类型由Int64变为Float64
2.9

julia> y.b = 2//3
2//3

julia> y
FooB(2.9, 0.6666666666666666)     # 有理数被提升为浮点数

可见,成员被成功修改了。

此外,由于y中的字段b限定了类型,所以即使修改时提供了别的类型,新的值也会保持类型不变。但这种操作需要保证提供的新值能被提升为限定的类型,否则会报错,例如:


julia> y.a = 3.2       # 字段a是Any类型,所以正常执行
3.2

julia> y.b = 3+2im     # 字段b限定为浮点型,但给定的复数型是比浮点型“更大”的类型,故错误
ERROR: InexactError: Float64(Float64, 3 + 2im) 

但是,可以将成员的类型限定为抽象类型,以提高其相容性,例如:


julia> mutable struct FooC
         a::Int32
         b::Real                    # 限定为抽象类型,可以为实数的任意子类型
       end

julia> w = FooC(1, 2); 

julia> typeof(w.b)
Int64                                   # 实际类型是Int64

julia> w.b = 3.2;

julia> typeof(w.b)
Float64                                   # 类型被改变

其中,定义的FooC成员b限定为Real类型,所以赋值为Int64或Float64类型均可。显然,成员b仍不能赋值为复数型,即:


julia> w.b = 3+2im     # Complex{Int64}
ERROR: InexactError: Real(Real, 3 + 2im) 

会报错,因为复数不是Real类型的子类型。

如果成员有复合类型的Bar,将上例中的FooB对象y作为参数对其实例化,例如:


julia> z = Bar(y, 20)
Bar(FooB(2.9, 0.6666666666666666), 20)

详细查看其成员内容,如下:


julia> z.m
FooB(2.9, 0.6666666666666666)

julia> z.n
20

同样,Bar对象z也是不可变的:


julia> z.m = FooB(2.3, 3.8)
ERROR: type Bar is immutable

julia> z.n = 30
ERROR: type Bar is immutable

但其内部的FooB却是可变的,即:


julia> z.m.a = 2.5
2.5

julia> z.m.b = 5.0
5.0

可见,struct结构的标识符mutable只控制着本层的不可变性,不会波及内层或外层,即不可变性不会在上下层之间传播。

不可变的复合类型是Julia对结构内权限进行控制的一种方式。相对于可变复合类型,编译器在处理不可变类型时能够高效地处理内部的存储结构,而且能够推导出代码中使用了哪些不可变对象,从而提高了编译效率。

另外,正因为不可变对象内部成员的值是恒定的,所以值的组合便可用于区分不同的对象。相对地,可变对象的内容随时都会改变,不变的仅是其在堆中的内存地址,所以地址是区分它们的唯一可信的依据。更需要注意的一点是,不可变对象在赋值或函数传参时均采用“值拷贝”的方式,而可变对象则会采用引用的方式。

提示 在开发过程中,使用可变对象还是不可变对象,可以参考以下两点:

·当两个对象具备相同的成员值集合时,是否需要识别为不同的事物。

·对象之间是否会随时独立地进行值的变换。

如果以上的答案都是“否”,则建议使用不可变类型。