屬性與字段
在kotlin中聲明屬性可以用var,val
聲明,其中var
聲明的是一個(gè)可變的變量,val
聲明是只讀的,不可修改
在IDE中,給
val
聲明的屬性賦值會(huì)報(bào)錯(cuò)
屬性的get和set方法
在kotlin中其實(shí)也是有g(shù)et和set方法的,不過(guò)一般都是默認(rèn)實(shí)現(xiàn)的,這個(gè)比java
方便
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
其初始器(initializer)、getter 和 setter 都是可選的。屬性類(lèi)型如果可以從初始器 (或者從其 getter 返回值橙弱,如下文所示)中推斷出來(lái),也可以省略翅雏。
var allByDefault: Int? // 錯(cuò)誤:需要顯式初始化器,隱含默認(rèn) getter 和 setter
這種展示是不可以的,必須要在`init`中初始化才可以
var initialized = 1 // 類(lèi)型 Int人芽、默認(rèn) getter 和 setter
這個(gè)的意思就是一個(gè)屬性必須要有已經(jīng)初始化之后才會(huì)有默認(rèn)的get和set方法,如果沒(méi)有初始化,IDE就會(huì)報(bào)錯(cuò)
get和set的多種實(shí)現(xiàn)
var ag1: Int = 0
get() {
return field
}
set(value) {
field = value
}
幕后字段
// 錯(cuò)誤的示范
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的值賦值到了屬性上望几,但是如果我們這樣寫(xiě)就會(huì)陷入死循環(huán),我們?cè)趕et方法里給屬性賦值萤厅,在kotlin里就會(huì)再次調(diào)用屬性的set方法橄抹,陷入循環(huán),
// 錯(cuò)誤的示范
class NetworkConfig(override var i: Int = 0) : Base() {
init {
ag = 0
}
var ag1: Int = 0
get() {
return ag1(再次調(diào)用get方法)
}
set(value) {
ag1 = value(再次調(diào)用說(shuō)set方法)
}
fun ss() {
// ag = 99
}
}
在 Kotlin 類(lèi)中不能直接聲明字段惕味。然而楼誓,當(dāng)一個(gè)屬性需要一個(gè)幕后字段時(shí),Kotlin 會(huì)自動(dòng)提供名挥。這個(gè)幕后字段可以使用field標(biāo)識(shí)符在訪(fǎng)問(wèn)器中引用:
幕后屬性
如果你的需求不符合這套“隱式的幕后字段”方案疟羹,那么總可以使用 幕后屬性(backing property):
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // 類(lèi)型參數(shù)已推斷出
}
return _table ?: throw AssertionError("Set to null by another thread")
}
編譯期常量
如果只讀屬性的值在編譯期是已知的,那么可以使用 const 修飾符將其標(biāo)記為編譯期常量禀倔。 這種屬性需要滿(mǎn)足以下要求:
位于頂層或者是 object 聲明 或 companion object 的一個(gè)成員
以 String 或原生類(lèi)型值初始化
沒(méi)有自定義 getter
這些屬性可以用在注解中:
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
//作用和final相似,要不就是在一個(gè)類(lèi)的頂層,要不就是靜態(tài)屬性
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… }
延遲初始化屬性與變量
一般地榄融,屬性聲明為非空類(lèi)型必須在構(gòu)造函數(shù)中初始化。 然而救湖,這經(jīng)常不方便愧杯。例如:屬性可以通過(guò)依賴(lài)注入來(lái)初始化, 或者在單元測(cè)試的 setup 方法中初始化鞋既。 這種情況下民效,你不能在構(gòu)造函數(shù)內(nèi)提供一個(gè)非空初始器。 但你仍然想在類(lèi)體中引用該屬性時(shí)避免空檢測(cè)涛救。
為處理這種情況畏邢,你可以用 lateinit 修飾符標(biāo)記該屬性:
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // 直接解引用
}
}
該修飾符只能用于在類(lèi)體中的屬性(不是在主構(gòu)造函數(shù)中聲明的 var 屬性,并且僅當(dāng)該屬性沒(méi)有自定義 getter 或 setter 時(shí))检吆,而自 Kotlin 1.2 起舒萎,也用于頂層屬性與局部變量。該屬性或變量必須為非空類(lèi)型蹭沛,并且不能是原生類(lèi)型臂寝。
在初始化前訪(fǎng)問(wèn)一個(gè) lateinit 屬性會(huì)拋出一個(gè)特定異常,該異常明確標(biāo)識(shí)該屬性被訪(fǎng)問(wèn)及它沒(méi)有初始化的事實(shí)摊灭。
所以我們?cè)谛枰臅r(shí)候可以檢測(cè)這個(gè)屬性是否初始化
if (foo::bar.isInitialized) {
println(foo.bar)
}
此檢測(cè)僅對(duì)可詞法級(jí)訪(fǎng)問(wèn)的屬性可用咆贬,即聲明位于同一個(gè)類(lèi)型內(nèi)、位于其中一個(gè)外圍類(lèi)型中或者位于相同文件的頂層的屬性帚呼。