<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 屬性與字段 參考[Java中的屬性和字段](http://www.hmoore.net/alex_wsc/java/1328553) [TOC] ## 聲明屬性 Kotlin 類中的屬性既可以用關鍵字 *var* 聲明為可變的,也可以用關鍵字 *val*聲明為只讀的。 ```kotlin class Address { var name: String = "Holmes, Sherlock" var street: String = "Baker" var city: String = "London" var state: String? = null var zip: String = "123456" } ``` 要使用一個屬性,只要用名稱引用它即可(和使用Java類字段(成員變量)差不多): ```kotlin fun copyAddress(address: Address): Address { val result = Address() // Kotlin 中沒有“new”關鍵字 result.name = address.name // 將調用訪問器 result.street = address.street // …… return result } ``` * 用`val`修飾符修飾的屬性是只讀的,即不能被修改,只可使用 * 用`var`修飾符修飾的屬性是可讀寫的,即能用能改 示例: ``` class Mime{ val id : String = "123" var name : String? = "kotlin" var age : Int? = 22 var sex : String? = "男" var weight : Float = 120.3f private var test : String = "" get() = "123" set(value){field = value} } fun main(args: Array<String>) { val mime = Mime() println("id = ${mime.id} \t name = ${mime.name} \t age = ${mime.age}\t sex = ${mime.sex} \t weight = ${mime.weight}") } ``` 輸出結果 ``` id = 123 name = kotlin age = 22 sex = 男 weight = 120.3 ``` ## Getters(讀訪問器) 與 Setters(寫訪問器) 在Kotlin中`getter`和`setter`?跟Java 中的getXX 和 setXX方法作用一樣,叫做**訪問器**。 > getter 叫讀訪問器,setter叫寫訪問器。`val`?聲明的變量只有讀訪問器getter,`var`聲明的變量讀寫訪問器都有。 聲明一個屬性的完整語法是 ``` // 可變屬性 var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>] // 只讀屬性 val <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] ``` 其初始器(initializer)、getter 和 setter 都是可選的。 屬性類型PropertyType如果可以從初始器(或者從其 getter 返回值,如下文所示)中推斷出來,也可以省略。 例如: ```kotlin var allByDefault: Int? // 錯誤:需要顯式初始化器,隱含默認 getter 和 setter var initialized = 1 // 能推斷出屬性類型Int,使用默認的getter?和setter ``` 一個只讀屬性的語法和一個可變的屬性的語法有兩方面的不同: 1、只讀屬性的用 `val`開始代替`var` 2、只讀屬性不允許 setter ```kotlin val simple: Int? // 類型 Int、默認 getter、必須在構造函數中初始化 val inferredType = 1 // 類型 Int 、默認 getter ``` 在Kotlin中,`getter`、`setter`?是屬性聲明的一部分,聲明一個屬性默認提供`getter`和`setter`?,當然了,如果有需要,你也可以自定義`getter`和`setter`。 * [ ] 在Kotlin 中,訪問一個屬性的實質是什么呢? 讀一個屬性,通過`.`表示,它的實質就是執行了屬性的getter訪問器,正如上面示例中的`mime.id`,**類似的,在Kotlin中,寫一個屬性的實質就是執行了屬性的寫訪問器setter**。示例如下 ``` class Person { var name:String = "Paul" set(value) { println("執行了寫訪問器,參數為:$value") } } //測試 fun main(args: Array<String>) { var person = Person() // 寫name屬性 person.name = "hi,this is new value" println("打印結果:${person.name}") } ``` 運行結果 ``` 執行了寫訪問器,參數為:hi,this is new value 打印結果:Paul ``` 可以看到給一個給一個屬性賦值時,確實是執行了寫訪問器setter,但是為什么結果還是默認值Paul呢?因為我們重寫了setter,卻沒有給屬性賦值,當然還是默認值。如果需要給屬性賦值,則應如下操作:更改set方法 ``` set(value)?{ println("執行了寫訪問器,參數為:$value") this.name?=?value } ``` 但是,如果這樣運行,會報錯oom:`Your program produces too much output!`,一運行就會報錯,直接StackOverFlow了,內存溢出,為什么呢?轉換為Java代碼看一下你就明白了,將Person類轉為Java類: ``` public final class Person { @NotNull private String name = "Paul"; @NotNull public final String getName() { return this.name; } public final void setName(@NotNull String value) { this.setName(value); } } ``` 我們可以從set方法中看到,方法循環調用了,`setName`?中又調用了`setName`?,死循環了,直到內存溢出,程序崩潰。Kotlin代碼也一樣,在setter中又給屬性賦值,導致一直執行setter, 陷入死循環,直到內存溢出崩潰。那么這個怎么解決了?這就引入了Kotlin一個重要的東西**幕后字段(下面會講解到,這里留個疑問)**。 ### 自定義Getter()與Setter() 我們可以為屬性自定義訪問器。如果我們定義了一個自定義的 getter,那么每次訪問該屬性時都會調用它(這讓我們可以實現計算出的屬性)。以下是一個自定義 getter 的示例: ```kotlin val isEmpty: Boolean get() = this.size == 0 ``` * [ ] **示例1**:用`val`修飾的屬性自定義情況 ``` class Mime{ // size屬性 private val size = 0 // 即isEmpty這個屬性,是判斷該類的size屬性是否等于0 val isEmpty : Boolean get() = this.size == 0 // 另一個例子 val num = 2 get() = if (field > 5) 10 else 0 } // 測試 fun main(args: Array<String>) { val mime = Mime() println("isEmpty = ${mime.isEmpty}") println("num = ${mime.num}") } ``` 運行結果 ``` isEmpty = true num = 0 ``` 如果我們自定義了一個setter,那么每次給屬性賦值時都會調用它。一個自定義的 setter 如下所示: ```kotlin var stringRepresentation: String get() = this.toString() set(value) { setDataFromString(value) // 解析字符串并賦值給其他屬性 } ``` 按照慣例,setter 參數的名稱是 `value`,但是如果你喜歡也可以選擇一個不同的名稱(`value`是`Koltin`寫`setter()`函數時其參數的約定俗成的習慣。你也可以換成其他的值)。自 Kotlin 1.1 起,如果可以從 getter 推斷出屬性類型,則可以省略它: ```kotlin val isEmpty get() = this.size == 0 // 具有類型 Boolean ``` * [ ] **示例2**:用`var`修飾的屬性自定義情況 ``` class Mime{ var str1 = "test" get() = field // 這句可以省略,kotlin默認實現的方式 set(value){ field = if (value.isNotEmpty()) value else "null" } var str2 = "" get() = "隨意怎么修改都不會改變" set(value){ field = if (value.isNotEmpty()) value else "null" } } // 測試 fun main(args: Array<String>) { val mime = Mime() println("str = ${mime.str1}") mime.str1 = "" println("str = ${mime.str1}") mime.str1 = "kotlin" println("str = ${mime.str1}") println("str = ${mime.str2}") mime.str2 = "" println("str = ${mime.str2}") mime.str2 = "kotlin" println("str = ${mime.str2}") } ``` 運行結果 ``` str = test str = null str = kotlin str = 隨意怎么修改都不會改變 str = 隨意怎么修改都不會改變 str = 隨意怎么修改都不會改變 ``` * [ ] **結論**: 1. 使用了`val`修飾的屬性,不能有`setter()`. 2. 不管是`val`還是`var`修飾的屬性,只要存在`getter()`,其值再也不會變化 3. 使用`var`修飾的屬性,可以省略掉`getter()`,不然`setter()`毫無意義。當然`get() = field`除外。而`get() = field`是`Koltin`默認的實現,是可以省略這句代碼的。 PS:在實際的項目開發中,這個自定義的`getter`與`setter`的意義不是太大。 ### 修改訪問器的可見性 如果你需要改變一個訪問器的可見性或者需要對其注解,但是不需要改變默認的實現,你可以定義訪問器而不定義其實現: ```kotlin var setterVisibility: String = "abc" private set // 此 setter 是私有的并且有默認實現,私有,不改變默認實現 var setterWithAnnotation: Any? = null @Inject set // 用 Inject 注解此 setter,注解,不改變默認實現 ``` 示例: ``` var str1 = "kotlin_1" private set // setter()訪問器的私有化,并且它擁有kotlin的默認實現 var test : String? @Inject set // 用`Inject`注解去實現`setter()` val str2 = "kotlin_2" private set // 編譯錯誤,因為val修飾的屬性,不能有setter var str3 = "kotlin_3" private get // 編譯出錯,因為不能有getter()的訪問器可見性 fun main(args: Array<String>) { // 這里偽代碼 str1 = "能不能重新賦值呢?" // 編譯出錯,因為上面的setter是私有的 } ``` **注意**:如果,屬性訪問器的可見性修改為`private`或者該屬性直接使用`private`修飾時,我們只能手動提供一個公有的函數去修改其屬性了。就像`Java`中的`Bean`的`setXXXX()`,另外,修改屬性訪問器在實際的開發中其實也沒有太大的作用。 >[success] 注意, > 1、不存在`Getter()與Setter()`的,這只是`Kotlin`中的叫法而已,真正的寫法,還是用`get()、set()` > 2、在`Kotlin`中,普通的類中一般是不提供`getter()`與`setter()`函數的,因為在普通的類中幾乎用不到,這一點和`Java`是相同的,但是`Java`中在定義純粹的數據類時,會用到`get()`與`set()`函數,但是`Kotlin`專門這種情況定義了`數據類`,這個特征。而`數據類`是系統已經為我們實現了`get()`和`set()`函數。 ## 幕后字段 **在 Kotlin 類中不能直接聲明字段(fields)。然而,當一個屬性需要一個幕后字段時,Kotlin 會自動提供**。這個幕后字段可以**使用`field`標識符在訪問器中引用**,或者換一個說法就是,**在Kotlin中,如果屬性至少一個訪問器使用默認實現,那么Kotlin會自動提供幕后字段,用關鍵字`field`表示,幕后字段主要用于自定義getter和setter中,并且只能在getter 和setter中訪問**: ```kotlin var counter = 0 // 注意:這個初始器直接為幕后字段賦值 set(value) { if (value >= 0) field = value } ``` ``` var count = 0 // 初始化值會直接寫入備用字段 set(value){ field = if(value > 10) value else 0 // 通過field來修改屬性的值。 } ``` `field` 標識符只能用在屬性的訪問器內。 如果屬性至少一個訪問器使用默認實現,或者自定義訪問器通過 `field` 引用幕后字段,將會為該屬性生成一個幕后字段。 例如,下面的情況下,不會生成幕后字段的屬性,就沒有幕后字段: ```kotlin val size = 0 /* 沒有幕后字段的原因: 1. 并且`getter()`不是默認的實現。沒有使用到`field`標識符 2. 使用`val`修飾,故而不存在默認的`setter()`訪問器,也沒有`field`修飾符 */ val isEmpty: Boolean get() = this.size == 0 ``` >[info] 注意:不管是幕后字段或者下面的幕后屬性,都是`Kotlin`對于空指針的一種解決方案,可以避免函數訪問私有屬性而破壞它的結構。 正如前面我們講到的在[Getters(讀訪問器) 與 Setters(寫訪問器)](http://www.hmoore.net/alex_wsc/android_kotlin/1318250#Getters__Setters_49)這一小節中,自定義寫訪問器中, ``` set(value)?{ println("執行了寫訪問器,參數為:$value") this.name?=?value } ``` 這樣會陷入死循環(原因見前面),這時就需要幕后字段,給幕后字段field賦值 ``` class Person { //錯誤的演示 var name = "" set(value) { field = value } } ``` getter 也一樣,返回了幕后字段: ``` // 例子一 class Person { var name:String = "" get() = field set(value) { field = value } } // 例子二 class Person { var name:String = "" } ``` 上面兩個屬性的聲明是等價的,例子一中的`getter`和`setter`?就是默認的`getter`和`setter`。其中幕后字段`field`指的就是當前的這個屬性,它不是一個關鍵字,只是在setter和getter的這個兩個特殊作用域中有著特殊的含義,就像一個類中的`this`,代表當前這個類。 用幕后字段,我們可以在getter和setter中做很多事,一般用于讓一個屬性在不同的條件下有不同的值,比如下面這個場景 **場景:**?我們可以根據性別的不同,來返回不同的姓名 ``` class Person(var gender:Gender){ var name:String = "" set(value) { field = when(gender){ Gender.MALE -> "Jake.$value" Gender.FEMALE -> "Rose.$value" } } } enum class Gender{ MALE, FEMALE } fun main(args: Array<String>) { // 性別MALE var person = Person(Gender.MALE) person.name="Love" println("打印結果:${person.name}") //性別:FEMALE var person2 = Person(Gender.FEMALE) person2.name="Love" println("打印結果:${person2.name}") } ``` 打印結果 ``` 打印結果:Jake.Love 打印結果:Rose.Love ``` 如上,我們實現了name 屬性通過gender 的值不同而行為不同。幕后字段大多也用于類似場景。 是不是Kotlin 所有屬性都會有幕后字段呢?當然不是,需要滿足下面條件之一: * 使用默認 getter / setter 的屬性,一定有幕后字段。對于 var 屬性來說,只要 getter / setter 中有一個使用默認實現,就會生成幕后字段; * 在自定義 getter / setter 中使用了 field 的屬性 正如下面的示例就沒有幕后字段 ``` class?NoField?{ var?size?=?0 //isEmpty沒有幕后字段 var?isEmpty get()?=?size?==?0 set(value)?{ ????????????size?\*=?2 ????????} } ``` 如上,`isEmpty`是沒有幕后字段的,重寫了setter和getter,沒有在其中使用?`field`,這或許有點不好理解,我們把它轉換成Java代碼看一下你可能就明白了,Java 代碼如下 ``` public final class NoField { private int size; public final int getSize() { return this.size; } public final void setSize(int var1) { this.size = var1; } public final boolean isEmpty() { return this.size == 0; } public final void setEmpty(boolean value) { this.size *= 2; } } ``` 看到沒,翻譯成Java代碼,只有一個size變量,isEmpty 翻譯成了?`isEmpty()`和`setEmpty()`兩個方法。返回值取決于size的值。 **有幕后字段的屬性轉換成Java代碼一定有一個對應的Java變量** ## 幕后屬性 ### 官方文檔介紹 如果你的需求不符合這套“隱式的幕后字段”方案,那么總可以使用 *幕后屬性(backing property)*: ```kotlin 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") } ``` > **對于 JVM 平臺**:通過默認 getter 和 setter 訪問私有屬性會被優化,所以本例不會引入函數調用開銷。 ### 個人見解 有時候有這種需求,我們希望一個屬性:**對外表現為只讀,對內表現為可讀可寫**,我們將這個屬性成為**幕后屬性**。 如: ``` 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") } ``` 將`_table`屬性聲明為`private`,因此外部是不能訪問的,內部可以訪問,外部訪問通過`table`屬性,而`table`屬性的值取決于`_table`,這里`_table`就是幕后屬性。 幕后屬性這種設計在Kotlin 的的集合Collection中用得非常多,Collection 中有個`size`字段,`size`?對外是只讀的,`size`的值的改變根據集合的元素的變換而改變,這是在集合內部進行的,這用幕后屬性來實現非常方便。 如Kotlin?`AbstractList`中`SubList`源碼: ``` private class SubList<out E>(private val list: AbstractList<E>, private val fromIndex: Int, toIndex: Int) : AbstractList<E>(), RandomAccess { // 幕后屬性 private var _size: Int = 0 init { checkRangeIndexes(fromIndex, toIndex, list.size) this._size = toIndex - fromIndex } override fun get(index: Int): E { checkElementIndex(index, _size) return list[fromIndex + index] } override val size: Int get() = _size } ``` `AbstractMap`?源碼中的keys 和 values 也用到了幕后屬性 ``` /** * Returns a read-only [Set] of all keys in this map. * * Accessing this property first time creates a keys view from [entries]. * All subsequent accesses just return the created instance. */ override val keys: Set<K> get() { if (_keys == null) { _keys = object : AbstractSet<K>() { override operator fun contains(element: K): Boolean = containsKey(element) override operator fun iterator(): Iterator<K> { val entryIterator = entries.iterator() return object : Iterator<K> { override fun hasNext(): Boolean = entryIterator.hasNext() override fun next(): K = entryIterator.next().key } } override val size: Int get() = this@AbstractMap.size } } return _keys!! } @kotlin.jvm.Volatile ``` ## 編譯期常量 已知值的屬性可以使用 *const*修飾符標記為 _編譯期常量_。這些屬性需要滿足以下要求: * 位于頂層或者是 [object聲明](http://www.kotlincn.net/docs/reference/object-declarations.html#%E5%AF%B9%E8%B1%A1%E5%A3%B0%E6%98%8E) 或 [companion object(伴生對象)](http://www.kotlincn.net/docs/reference/object-declarations.html#%E4%BC%B4%E7%94%9F%E5%AF%B9%E8%B1%A1) 的一個成員 * 以 `String` 或原生類型值初始化(初始化為`String`類型或基本類型的值) * 沒有自定義 getter 如 ``` const val CONST_NUM = 5 const val CONST_STR = "Kotlin" ``` 這些屬性可以用在注解中: ```kotlin const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated" @Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… } ``` 可參考:[Kotlin之常量的用法](http://www.hmoore.net/alex_wsc/android_kotlin/1037791#Kotlin_170) ## 延遲初始化屬性與變量 **一般地,屬性聲明為非空類型必須在構造函數中初始化**。然而,這經常不方便。例如:**屬性可以通過依賴注入來初始化,或者在單元測試的 setup 方法中初始化。 這種情況下,你不能在構造函數內提供一個非空初始器。但你仍然想在類體中引用該屬性時避免空檢測。** 為處理這種情況,你**可以用 `lateinit` 修飾符標記該屬性**: ```kotlin public class MyTest { lateinit var subject: TestSubject @SetUp fun setup() { subject = TestSubject() } @Test fun test() { subject.method() // 直接解引用 } } ``` ***** 該修飾符只能用于在類體中的屬性(不是在主構造函數中聲明的 `var` 屬性,并且僅當該屬性沒有自定義 getter 或 setter 時),而自 Kotlin 1.2 起,也用于頂層屬性與局部變量。該屬性或變量必須為非空類型,并且不能是原生類型(原生類型指 Int、Float、Double、Short等,String類型是可以的)。 ***** 在初始化前訪問一個 `lateinit` 屬性會拋出一個特定異常,該異常明確標識該屬性被訪問及它沒有初始化的事實。 ### 檢測一個 lateinit var 是否已初始化(自 1.2 起) 要檢測一個 `lateinit var` 是否已經初始化過,請在[該屬性的引用](http://www.kotlincn.net/docs/reference/reflection.html#%E5%B1%9E%E6%80%A7%E5%BC%95%E7%94%A8)上使用`.isInitialized`: ```kotlin if (foo::bar.isInitialized) { println(foo.bar) } ``` 此檢測僅對可詞法級訪問的屬性可用,即聲明位于同一個類型內、位于其中一個外圍類型中或者位于相同文件的頂層的屬性。 ## 覆蓋屬性 參見[覆蓋屬性](http://www.kotlincn.net/docs/reference/classes.html#%E8%A6%86%E7%9B%96%E5%B1%9E%E6%80%A7) ## 委托屬性 最常見的一類屬性就是簡單地從幕后字段中讀取(以及可能的寫入)。另一方面,使用自定義 getter 和 setter 可以實現屬性的任何行為。介于兩者之間,屬性如何工作有一些常見的模式。一些例子:惰性值、通過鍵值從映射讀取、訪問數據庫、訪問時通知偵聽器等等。 這些常見行為可以通過使用[_委托屬性_](http://www.kotlincn.net/docs/reference/delegated-properties.html)實現為庫。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看