1.两个关键字
Kotlin的类和接口都可以有属性,修饰属性的关键字有var
(可变的)和val
(不可变的)。
1.1var
对于var
来说,它具有setter和getter,声明一个属性的完整语法是:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
比如:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // 解析字符串并赋值给其他属性
}
自 Kotlin 1.1 起,如果可以从 getter 推断出属性类型:
val isEmpty get() = this.size == 0 // 具有类型 Boolean
1.1.1自定义setter和getter
一般情况下,我们不需要对setter和getter进行自定义,编译器已经默认实现了它们,如果需要自定义的话如下:
class ExampleUnitTest {
var visibal: String = "aa"
get() = field
set(value) {
field = value + "cc"
}
@Test
fun testMain() {
println(visibal)//输出aa
visibal = "bb"
println(visibal)//输出bbcc
}
}
其中的field
称为幕后字段
,它只能在setter和getter中访问,如果不通过field改变值,使用如下方式会报堆栈溢出,因为会递归调用setter
var visibal: String = "aa"
get() = field
set(value) {
visibal = "aaa"
}
1.2val
val只有getter没有setter
2.接口中的属性
上面介绍的属性是基于类的,你也可以在接口中定义属性。在接口中声明的属性要么是抽象的,要么提供访问器的实现。在接口中声明的属性不能有幕后字段(backing field),因此接口中声明的访问器不能引用它们。
interface MyInterface {
val prop: Int // 抽象的
//声明了访问器
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
class Child : MyInterface {
override val prop: Int = 29
}
3.属性的覆盖 力求清晰显式。与 Java 不同,Kotlin 需要显式标注可覆盖的成员(我们称之为开放)以及覆盖后的成员:
open class Foo {
open val x: Int get() { …… }
}
class Bar1 : Foo() {
override val x: Int = ……
}
你也可以用一个 var 属性覆盖一个 val 属性,但反之则不行。这是允许的,因为一个 val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法。
请注意,你可以在主构造函数中使用 override 关键字作为属性声明的一部分。
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}