在Kotlin中, var
是可变的, val
应该只分配一次 .
但是,请考虑以下示例中的 val foo
:
var counter = 0
val foo: String
get(){
counter++
return "val$counter"
}
fun main(): String {
val a = foo
val b = foo
val c = foo
return "we got: $a $b $c"
// output: we got: val1 val2 val3
}
每次我们尝试访问 foo
时都会执行 get()
方法,从而产生 different values for val .
由于 foo
的值正在变化,我尝试使用 var
. 然后编译器抱怨"Property must be initialized" . 所以我必须给它一个默认值:
var foo: String = "default value that will never be used"
get(){
counter++
return "val$counter"
}
我不喜欢这里的任何一种方法 . 什么是正确的做法?
2 回答
对于局部变量,是的 . 对于属性,不是真的:
val
表示"only has a getter",var
表示"has both a getter and a setter" . 这个getter(和setter)可以做任何事情 . 例如,您每次都可以返回一个随机值 .一个例外是重新分配
val
的支持字段:不会编译 .
这已在YouTrack中报告为KT-16681,"kotlin allows mutating the field of read-only property" .
正如您在KT-16681的回复中所看到的,自定义getter被编译成另一个函数,这使得字段
foo
和方法getFoo()
成为两个不相关的东西 .同样来自KT-16681的回复,这种违规(通过支持字段重新分配只读属性)将产生自Kotlin 1.3以来的错误 .
Update: 在评论中,原始海报提到
KT-16681
与此问题不同 . 但是,受到这个问题的启发,我们可以通过Tools -> Kotlin -> Show Kotlin Bytecode
(删除元数据等)看到Kotlin字节码:正如我们所看到的,
foo
没有字段,只有getFoo()
,比较正常的val
声明:使用
val foo = "aaa"
将生成一个普通的final static String foo
字段和final static String getFoo()
方法,但使用带有get()
的val foo: String
将不会生成该字段,只需生成一个方法 . 这个getter函数是由Kotlin生成的,我相信丢失的字段来自于val
的声明中丢失的初始赋值,但是我找不到真正的文档,就像Getters and Setters in Kotlin这样直接使用这个结论 .所以,这似乎是修改
final static
的旁路 .当不可变字段引用可变字段时会出现问题 .
val
不可重新分配,但它引用了var
,一个可重新分配的字段,这导致val
的修改 .