为什么不可变的 class 在 Groovy 中是可变的?

Why an immutable class is mutable in Groovy?

阅读 Benjamin J. Evans 和 Martijn Verburg 合着的 "The Well-Grounded Java Developer" 一书,我在 Groovy 中遇到了以下示例:

class Character {
    private int strength
    private int wisdom
}

def pc = new Character(strength: 10, wisdom: 15)
pc.strength = 18
println "STR [" + pc.strength + "] WIS [" + pc.wisdom + "]"

片段的输出如下:

STR [18] WIS [15]

到目前为止没有问题。上面的代码已经用 @Immutable 注释稍作修改,正如书中所建议的:

import groovy.transform.*

@Immutable
class Character {
    private int strength
    private int wisdom
}

def pc = new Character(strength: 10, wisdom: 15)

pc.strength = 18

println "STR [" + pc.strength + "] WIS [" + pc.wisdom + "]"

最后一个片段的输出仍然是一样的:

STR [18] WIS [15]

预期的结果与上面的结果不同...

为什么不可变 class 的对象看起来像可变对象? Groovy 中的不变性概念是否允许修改 class 字段?

要解决您报告的问题很简单,就是删除 access modifier to fields。这应该可以解决问题。

import groovy.transform.Immutable
@Immutable
class Character {
    int strength
    int wisdom
}

def pc = new Character(strength: 10, wisdom: 15)

pc.strength = 18

println "STR [" + pc.strength + "] WIS [" + pc.wisdom + "]"

现在,它会引发异常,如下所示:

groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: strength for class: Character
at Character.setProperty(Script1.groovy)
at Script1.run(Script1.groovy:10)

如果您想了解行为不同原因的更多详细信息,请参阅 here

当显式将字段设置为 private 时,此行为是预期的。 Immutable 状态的文档:

You don't have to follow Groovy's normal property conventions, e.g. you can create an explicit private field and then you can write explicit get and set methods. Such an approach, isn't currently prohibited (to give you some wiggle room to get around these conventions) but any fields created in this way are deemed not to be part of the significant state of the object and aren't factored into the equals or hashCode methods.