Kotlin小记-4 属性

284 阅读2分钟

声明一个属性的完整语法是

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

幕后字段

在 Kotlin 类中不能直接声明字段。

class Person {
    //错误的演示
    var name = ""
        set(value) {
            this.name = value
        }
}

如果属性至少一个访问器使用默认实现,那么Kotlin会自动提供幕后字段,用关键字field表示,幕后字段主要用于自定义getter和setter中,并且只能在getter和setter中访问。

var counter = 0 // 注意:这个初始器直接为幕后字段赋值
    set(value) {
        if (value >= 0) field = value
    }

field 标识符只能用在属性的访问器内。

幕后属性

如果你的需求不符合这套“隐式的幕后字段”方案,那么总可以使用 幕后属性(backing property): 我们希望一个属性:对外表现为只读,对内表现为可读可写,我们将这个属性成为幕后属性

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // 类型参数已推断出
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

将_table属性声明为private,因此外部是不能访问的,内部可以访问,外部访问通过table属性,而table属性的值取决于_table,这里_table就是幕后属性。

延迟初始化属性与变量

一般地,属性声明为非空类型必须在构造函数中初始化 ,如果想做延时初始化 可以用 lateinit 修饰符标记该属性:(然而,这经常不方便。例如:属性可以通过依赖注入来初始化, 或者在单元测试的 setup 方法中初始化)

public class MyTest {
    lateinit var subject: TestSubject
}

要检测一个 lateinit var 是否已经初始化过,在该属性的引用上使用 .isInitialized:

if (foo::bar.isInitialized) {
    println(foo.bar)
}

此检测仅对可词法级访问的属性可用,即声明位于同一个类型内、位于其中一个外围类型中或者位于相同文件的顶层的属性