属性的声明
Kotlin类中可以有属性,属性可以被声明为可变属性,用var
关键字修饰;或者声明为只读属性,用val
修饰。
class Address {
var name: String = ...
var street: String = ...
var city: String = ...
var state: String? = ...
var zip: String = ...
}
要使用一个属性,可以直接通过它的名字调用它:
fun copyAddress(address: Address): Address {
val result = Address()
result.name = address.name
result.street = address.street
// ...
return result
}
Getters 和 Setters
声明一个属性的完整语法如下:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
其中initializer
, getter
和 setter
都是可选项。如果属性的类型可推断(或者可以从getter的返回值类型推断),propertyType
也是可选项。
例子:
var allByDefault: Int? // 报错: 属性未被初始化
var initialized = 1 // 属性 type 为 Int, 生成默认 getter 和 setter
只读属性的声明和可变属性有些不一样,它没有setter,毕竟只读。
我们能自定义存取器,下面是一个自定义的getter:
val isEmpty: Boolean
get() = this.size == 0
一个自定义的setter如下:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value)
}
方便起见,setter参数的名字是value
,但这并不是关键字,你也可以给参数起其它名字。
如果需要改变存取器的可见性或者注释它,可以只定义存取器而不定义其实现,如下:
var setterVisibility: String = "abc"
private set // private 的 setter,拥有默认实现
var setterWithAnnotation: Any? = null
@Inject set // 被Inject注释的setter
(1). Backing Fields
这个不好翻译,看到有人译作幕后字段
,也有人译作影子字段
,它的作用是在存取器中代指属性本身。
Kotlin中不支持直接声明字段。然而,当一个属性需要backing field
的时候,Kotlin会自动为它提供。backing field在存取器中用标识符field
引用。
var counter = 0
set(value) {
if (value >= 0) field = value
}
注意:field
标识符只能在属性的存取器中被使用。
当一个属性默认实现至少一个存取器方法(getter/setter)或者在自定义存取器中使用field
关键字引用的时候,它就会生成一个backing field。下面这个例子就没有生成backing field:
val isEmpty: Boolean
get() = this.size == 0
(2). Backing Properties
如果backing field无法满足需求,那么可以使用backing properties:
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")
}
从各方面看,这与Java的方式一样。默认通过getter和setter访问私有属性的优化,能免去调用函数带来的开销。
编译时常量
在编译时,值已知的属性称为编译时常量,使用修饰符const
来标记。编译时常量需要满足以下要求:
- 位于顶层 或者 是object的成员
- 使用String或者原始数据类型初始化
- 没有自定义的getter方法
这样的属性可以被用在注解中:
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }
延迟初始化属性和变量
正常情况下,非空类型的属性必须在构造器中被初始化。然而,这样通常会不方便。比如:通过依赖注入,或者在单元测试的setup
方法中被初始化的属性。这种情况下,当在类体中引用属性时,就无法满足在构造器中非空初始化,并且同时又要避免空值检查。
为了处理这种情况,可以使用lateinit修饰符来标记属性:
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method()
}
}
使用限定:
lateinit
修饰符只能用于在类体中用var
声明的属性,并且属性不能有自定义的getter或setter方法,自从Kotlin 1.2,也可用于顶层的属性和局部变量。属性和变量必须为非空,而且不能是原始数据类型。
访问一个还没被初始化的lateinit
属性将会抛出错误。
检查一个 lateinit var 是否已被初始化(自从kotlin 1.2)
要检查一个lateinit var 是否已被初始化,可以在该属性的引用上使用.isInitialized方法。
if (foo::bar.isInitialized) {
println(foo.bar)
}
此检测仅对可词法级访问的属性可用,即声明位于同一个类型内、位于其中一个外围类型中或者位于相同文件的顶层的属性。
上面这段话不是特别明白 … …