属性与字段
在kotlin中声明属性可以用var,val
声明,其中var
声明的是一个可变的变量,val
声明是只读的,不可修改
在IDE中,给val
声明的属性赋值会报错
属性的get和set方法
在kotlin中其实也是有get和set方法的,不过一般都是默认实现的,这个比java
方便
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
其初始器(initializer)、getter 和 setter 都是可选的。属性类型如果可以从初始器 (或者从其 getter 返回值,如下文所示)中推断出来,也可以省略。
var allByDefault: Int? // 错误:需要显式初始化器,隐含默认 getter 和 setter
这种展示是不可以的,必须要在`init`中初始化才可以
var initialized = 1 // 类型 Int、默认 getter 和 setter
这个的意思就是一个属性必须要有已经初始化之后才会有默认的get和set方法,如果没有初始化,IDE就会报错
get和set的多种实现
var ag1: Int = 0
get() {
return field
}
set(value) {
field = value
}
幕后字段
// 错误的示范
class NetworkConfig(override var i: Int = 0) : Base() {
init {
ag = 0
}
var ag1: Int = 0
get() {
return ag1
}
set(value) {
ag1 = value
}
fun ss() {
// ag = 99
}
}
在这里我们好似是把get和set的值赋值到了属性上,但是如果我们这样写就会陷入死循环,我们在set方法里给属性赋值,在kotlin里就会再次调用属性的set方法,陷入循环,
// 错误的示范
class NetworkConfig(override var i: Int = 0) : Base() {
init {
ag = 0
}
var ag1: Int = 0
get() {
return ag1(再次调用get方法)
}
set(value) {
ag1 = value(再次调用说set方法)
}
fun ss() {
// ag = 99
}
}
在 Kotlin 类中不能直接声明字段。然而,当一个属性需要一个幕后字段时,Kotlin 会自动提供。这个幕后字段可以使用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")
}
编译期常量
如果只读属性的值在编译期是已知的,那么可以使用 const 修饰符将其标记为编译期常量。 这种属性需要满足以下要求:
位于顶层或者是 object 声明 或 companion object 的一个成员
以 String 或原生类型值初始化
没有自定义 getter
这些属性可以用在注解中:
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
//作用和final相似,要不就是在一个类的顶层,要不就是静态属性
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… }
延迟初始化属性与变量
一般地,属性声明为非空类型必须在构造函数中初始化。 然而,这经常不方便。例如:属性可以通过依赖注入来初始化, 或者在单元测试的 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 起,也用于顶层属性与局部变量。该属性或变量必须为非空类型,并且不能是原生类型。
在初始化前访问一个 lateinit 属性会抛出一个特定异常,该异常明确标识该属性被访问及它没有初始化的事实。
所以我们在需要的时候可以检测这个属性是否初始化
if (foo::bar.isInitialized) {
println(foo.bar)
}
此检测仅对可词法级访问的属性可用,即声明位于同一个类型内、位于其中一个外围类型中或者位于相同文件的顶层的属性。