在前面的章節中,詳細的爲你們講解到了Kotlin
中對類的類的定義、使用、初始化
、初始化、類繼承
等內容,可是在一個類中,幾乎上是不可能不出現屬性與字段(field)
的,這一篇文章就爲你們奉上Kotlin
中屬性與字段
的定義、使用及高級操做等。若是您目前對Kotlin
中的類沒有一個認知的話,請參見Kotlin——中級篇(一):類(class)詳解.html
一個類中是能夠存在屬性的,一個屬性能夠用
val
或者var
修飾。git
- 用
val
修飾符修飾的屬性是隻讀的,即不能被修改,只可以使用- 用
var
修飾符修飾的屬性是可讀寫的,即能用能改
例:github
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
注意:函數
`val`關鍵字爲只讀,`var`爲可讀寫。這裏舉例說明:
例: 仍是上面的例子post
// id是隻讀的,其餘的屬性是可讀寫的 mime.id = "123456" // 編輯器直接會報錯 mime.name = "kotlin2" // 能夠,由於是var修飾的 ...
getter()
對應Java
中的get()函數
,setter()
對應Java
中的set()函數
。不過注意的是,不存在Getter()與Setter()
的,這只是Kotlin
中的叫法而已,真是的寫法,仍是用get()、set()
。能夠看下面的例子。- 在
Kotlin
中,普通的類中通常是不提供getter()
與setter()
函數的,由於在普通的類中幾乎用不到,這一點和Java
是相同的,可是Java
中在定義純粹的數據類時,會用到get()
與set()
函數,可是Kotlin
專門這種狀況定義了數據類
,這個特徵。而數據類
是系統已經爲咱們實現了get()
和set()
函數。
這裏爲你們演示getter()
與setter()
單元測試
class Test{ /* * getter 和 setter是可選的 */ // 當用var修飾時,必須爲屬性賦默認值(特指基本數據類型,由於自定義的類型可使用後期初始化屬性,見後續) 即便在用getter()的狀況下,不過這樣寫出來,無論咱們怎麼去改變其值,其值都爲`123` var test1 : String = "" get() = "123" set(value){field = value} // 用val修飾時,用getter()函數時,屬性能夠不賦默認值。可是不能有setter()函數。 val test2 : String get() = "123" // 等價於:val test2 : String = "123" }
注意: 請認真看上面的註釋......測試
在上面的代碼中出現了set(value){field = value}
這樣的一句代碼。其中value
是Koltin
寫setter()
函數時其參數的約定俗成的習慣。你也能夠換成其餘的值。而field
是指屬性自己。優化
這裏講解屬性的自定義getter()
與setter()
。由上面可知,使用val
修飾的屬性,是不能有setter()
的。而使用var
修飾的屬性能夠同時擁有自定義的getter()
與setter()
。經過兩個實例來講明:this
例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
例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 = 隨意怎麼修改都不會改變
通過上面的實例,我總結出瞭如下幾點:
- 使用了
val
修飾的屬性,不能有setter()
.- 不論是
val
仍是var
修飾的屬性,只要存在getter()
,其值不再會變化- 使用
var
修飾的屬性,能夠省略掉getter()
,否則setter()
毫無心義。固然get() = field
除外。而get() = field
是Koltin
默認的實現,是能夠省略這句代碼的。
故而,在實際的項目開發中,這個自定義的getter
與setter
的意義不是太大。
若是您對
Kotlin
中的可見性修飾符還不瞭解的話,請參見Kotlin——中級篇(三):可見性修飾符詳解
修改屬性訪問器在實際的開發中其實也沒有太大的做用,下面舉個例子就明白了:
例:
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()
Kotlin的類不能有字段。 可是,有時在使用自定義訪問器時須要有一個後備字段。爲了這些目的,Kotlin提供了可使用字段標識符訪問的自動備份字段
例:
var count = 0 // 初始化值會直接寫入備用字段 set(value){ field = if(value > 10) value else 0 // 經過field來修改屬性的值。 }
注意:
field
標識符只能在屬性的訪問器中使用。這在上面提到過- 若是屬性使用至少一個訪問器的默認實現,或者自定義訪問器經過
field
標識符引用它,則將爲屬性生成後備字段。
看上面的例子,使用了getter()
的默認實現。又用到了field
標識符。
例:不會生成後備字段的屬性
val size = 0 /* 沒有後備字段的緣由: 1. 而且`getter()`不是默認的實現。沒有使用到`field`標識符 2. 使用`val`修飾,故而不存在默認的`setter()`訪問器,也沒有`field`修飾符 */ val isEmpty :Boolean get() = this.size == 0
不論是後備字段或者下面的後備屬性,都是Kotlin
對於空指針的一種解決方案,能夠避免函數訪問私有屬性而破壞它的結構。
這裏值得強調的一點是,setter()
中
所謂後備屬性,實際上是對後備字段
的一個變種,它實際上也是隱含試的對屬性值的初始化聲明,避免了空指針。
咱們根據一個官網的例子,進行說明:
private var _table: Map<String, Int>? = null public val table: Map<String, Int> get() { if (_table == null) { _table = HashMap() // 初始化 } // ?: 操做符,若是_table不爲空則返回,反之則拋出AssertionError異常 return _table ?: throw AssertionError("Set to null by another thread") }
從上面的代碼中咱們能夠看出:_table
屬性是私有的,咱們不能直接的訪問它。故而提供了一個公有的後備屬性(table
)去初始化咱們的_table
屬性。
通俗的講,這和在Java中定義Bean屬性的方式同樣。由於訪問私有的屬性的getter和setter函數,會被編譯器優化成直接反問其實際字段。所以不會引入函數調用開銷。
在開發中,不免會遇到一些已經肯定的值的量。在Java
中,咱們能夠稱其爲常量
。在kotlin
中,咱們稱其爲編譯時常數
。咱們能夠用const
關鍵字去聲明它。其一般和val
修飾符連用
- 關鍵字:
const
- 知足
const
的三個條件:
- 對象的頂層或成員,即頂層聲明。
- 初始化爲
String
類型或基本類型的值- 沒有定製的
getter()
例:
const val CONST_NUM = 5 const val CONST_STR = "Kotlin"
關於編譯時常數
這個知識點更詳細的用法,我在講解講解變量的定義這一章節時,已經詳細講解過了。這裏不作累贅。
您能夠參見個人Kotlin——初級篇(二):變量、常量、註釋這一篇文章
一般,聲明爲非空類型的屬性必須在構造函數中進行初始化。然而,這一般不方便。例如,能夠經過依賴注入或單元測試的設置方法初始化屬性。 在這種狀況下,不能在構造函數中提供非空的初始值設置,可是仍然但願在引用類的正文中的屬性時避免空檢查。故而,後期初始化屬性就應運而生了。
- 關鍵字 :
lateinit
- 該關鍵字必須聲明在類的主體內,而且只能是用
var
修飾的屬性。而且該屬性沒有自定義的setter()
與getter()
函數。- 該屬性必須是非空的值,而且該屬性的類型不能爲基本類型。
例:
class Test{ // 聲明一個User對象的屬性 lateinit var user : User /* 下面的代碼是錯誤的。由於用lateinit修飾的屬性,不能爲基本類型。 這裏的基本類型指 Int、Float、Double、Short等,String類型是能夠的 */ // lateinit var num : Int }
關於後期初始化屬性
這一個知識點,我在講解講解變量的定義這一章節時,已經詳細講解過了。這裏不作累贅。不過關於這一知識點,通常都是在Android
開發中或者在依賴注入時會用到。
您能夠參見個人Kotlin——初級篇(二):變量、常量、註釋這一篇文章
要想把委託屬性
這一個知識點詳細的講解明白。以及它的實現與實例都要花上很大的篇幅去講解。我會單獨抽出一篇文章去講解它,故而這裏就不作累述了。您能夠參見文章Kotlin——高級篇(八):委託與委託屬性詳解
在一些文章或者書籍裏面,關於Kotlin
屬性/字段的講解,不會有這麼多的知識點,而且咱們在實際開發中,也有一些知識點不多去用到。這裏你們主要去理解與實踐屬性的基本使用與定義、Getter()
與Setter()
、後期初始化屬性、以及編譯時常數這幾個點就能夠了。
若是各位大佬看了以後感受還闊以,就請各位大佬隨便star
一下,您的關注是我最大的動力。
個人我的博客:Jetictors
Github:Jteictors
掘金:Jteictors