- 在Kotlin中,属性和字段可以使用关键字
var
(可变属性)和关键字val
(不可变、只读属性),var
定义的属性默认有getter
和setter
,val
定义的属性只有getter
属性。
属性的定义
- 定义属性的完整语法如下:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
// 中文版说明
var <属性名>[: <属性类型>] [= <属性初始化器>]
[<getter>]
[<setter>]
- 在属性定义过程中,属性初始化器、
getter
、setter
都是可选的,但是如果属性没有初始化器,必须在构造函数或者初始化块中对属性进行初始化
class Teacher : Person {
// age属性未定义初始化器
var age: Int
init {
// 在初始化块中初始化
age = 0
}
constructor(name: String) : super(name) {
// 在构造函数中初始化
age = 0
}
}
没有初始化器的属性,只需要在初始化块
init
和构造函数这两者选其一初始化即可。
- 如果需要编写自己的
getter
和setter
,并在内部实现对属性进行赋值或者返回属性的值,切忌直接使用属性,应该使用幕后字段,否则会出现无限循环调用导致StackOverflowError
。
- 在 Kotlin 类中不能直接声明字段。然而,当一个属性需要一个幕后字段时,Kotlin 会自动提供。
- 幕后字段只能在属性的访问器(
getter
和setter
)中使用- 幕后字段通过
field
进行访问
class Teacher : Person {
// age属性未定义初始化器
var age: Int
get() = field // 如果此处使用return this.age,会导致StackOverflowError
set(value) {
// 如果此处使用this.age = value,会导致StackOverflowError
field = value
}
init {
// 在初始化块中初始化
age = 0
}
constructor(name: String) : super(name) {
// 在构造函数中初始化
age = 0
}
}
- 如果这套“隐式的幕后字段”方案不符合你的需求,那么总可以使用幕后属性(backing property)
class Teacher : Person {
// age属性未定义初始化器
var age: Int
get() = field
set(value) {
field = value
}
var _addr: String? = null // 幕后属性
val addr: String
get() {
if(null == _addr) {
_addr = "this is test backing property"
}
return _addr ?: throw AssertionError("Set to null by another thread")
}
init {
// 在初始化块中初始化
age = 0
}
constructor(name: String) : super(name) {
// 在构造函数中初始化
age = 0
}
}
编译期常量
编译期常量可以在注解中使用
已知值的属性可以使用 const 修饰符标记为编译期常量。 这些属性需要满足以下要求:
- 位于顶层或者是
object
声明 或companion object
的一个成员 - 以
String
或原生类型值初始化 - 没有自定义
getter
// 编译期常量,必须位于顶层,不能放到类内部
const val TYPE_HEADER: Int = 1
class Teacher : Person {
// age属性未定义初始化器
var age: Int
get() = field
set(value) {
field = value
}
var _addr: String? = null // 幕后属性
val addr: String
get() {
if(null == _addr) {
_addr = "this is test backing property"
}
return _addr ?: throw AssertionError("Set to null by another thread")
}
init {
// 在初始化块中初始化
age = 0
}
constructor(name: String) : super(name) {
// 在构造函数中初始化
age = 0
}
}
延迟初始化属性与变量
一般地,属性声明为非空类型必须在构造函数中初始化。 然而,这经常不方便。例如:属性可以通过依赖注入来初始化, 或者在单元测试的 setup
方法中初始化。 这种情况下,你不能在构造函数内提供一个非空初始器。 但你仍然想在类体中引用该属性时避免空检查。
为处理这种情况,你可以用lateinit
修饰符标记该属性:
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // 直接解引用
}
}
该修饰符只能用于在类体中的属性(不是在主构造函数中声明的 var 属性,并且仅当该属性没有自定义 getter 或 setter 时),而自 Kotlin 1.2 起,也用于顶层属性与局部变量。该属性或变量必须为非空类型,并且不能是原生类型。