Kotlin——中級篇(二): 屬性與字段詳解

在前面的章節中,詳細的爲你們講解到了Kotlin中對類的類的定義、使用、初始化、初始化、類繼承等內容,可是在一個類中,幾乎上是不可能不出現屬性與字段(field)的,這一篇文章就爲你們奉上Kotlin屬性與字段的定義、使用及高級操做等。若是您目前對Kotlin中的類沒有一個認知的話,請參見Kotlin——中級篇(一):類(class)詳解.html

目錄

1、屬性的基礎使用

一個類中是能夠存在屬性的,一個屬性能夠用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修飾的
...

2、Getter()與Setter()

  • 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}這樣的一句代碼。其中valueKoltinsetter()函數時其參數的約定俗成的習慣。你也能夠換成其餘的值。而field是指屬性自己。優化

2.一、自定義

這裏講解屬性的自定義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 = 隨意怎麼修改都不會改變

通過上面的實例,我總結出瞭如下幾點:

  1. 使用了val修飾的屬性,不能有setter().
  2. 不論是val仍是var修飾的屬性,只要存在getter(),其值不再會變化
  3. 使用var修飾的屬性,能夠省略掉getter(),否則setter()毫無心義。固然get() = field除外。而get() = fieldKoltin默認的實現,是能夠省略這句代碼的。

故而,在實際的項目開發中,這個自定義的gettersetter的意義不是太大。

2.二、修改訪問器的可見性

若是您對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中的BeansetXXXX()

3、後備字段

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()

4、後備屬性

所謂後備屬性,實際上是對後備字段的一個變種,它實際上也是隱含試的對屬性值的初始化聲明,避免了空指針。

咱們根據一個官網的例子,進行說明:

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函數,會被編譯器優化成直接反問其實際字段。所以不會引入函數調用開銷。

5、編譯時常數

在開發中,不免會遇到一些已經肯定的值的量。在Java中,咱們能夠稱其爲常量。在kotlin中,咱們稱其爲編譯時常數。咱們能夠用const關鍵字去聲明它。其一般和val修飾符連用

  • 關鍵字:const
  • 知足const的三個條件:
    1. 對象的頂層或成員,即頂層聲明。
    2. 初始化爲String類型或基本類型的值
    3. 沒有定製的getter()

例:

const val CONST_NUM = 5
const val CONST_STR = "Kotlin"

關於編譯時常數這個知識點更詳細的用法,我在講解講解變量的定義這一章節時,已經詳細講解過了。這裏不作累贅。

您能夠參見個人Kotlin——初級篇(二):變量、常量、註釋這一篇文章

6、後期初始化屬性

一般,聲明爲非空類型的屬性必須在構造函數中進行初始化。然而,這一般不方便。例如,能夠經過依賴注入或單元測試的設置方法初始化屬性。 在這種狀況下,不能在構造函數中提供非空的初始值設置,可是仍然但願在引用類的正文中的屬性時避免空檢查。故而,後期初始化屬性就應運而生了。

  • 關鍵字 : lateinit
  • 該關鍵字必須聲明在類的主體內,而且只能是用var修飾的屬性。而且該屬性沒有自定義的setter()getter()函數。
  • 該屬性必須是非空的值,而且該屬性的類型不能爲基本類型。

例:

class Test{
    // 聲明一個User對象的屬性
    lateinit var user : User   
    
    /*
        下面的代碼是錯誤的。由於用lateinit修飾的屬性,不能爲基本類型。
        這裏的基本類型指 Int、Float、Double、Short等,String類型是能夠的
    */    
    // lateinit var num : Int    
}

關於後期初始化屬性這一個知識點,我在講解講解變量的定義這一章節時,已經詳細講解過了。這裏不作累贅。不過關於這一知識點,通常都是在Android開發中或者在依賴注入時會用到。

您能夠參見個人Kotlin——初級篇(二):變量、常量、註釋這一篇文章

7、委託屬性

要想把委託屬性這一個知識點詳細的講解明白。以及它的實現與實例都要花上很大的篇幅去講解。我會單獨抽出一篇文章去講解它,故而這裏就不作累述了。您能夠參見文章Kotlin——高級篇(八):委託與委託屬性詳解

總結

在一些文章或者書籍裏面,關於Kotlin屬性/字段的講解,不會有這麼多的知識點,而且咱們在實際開發中,也有一些知識點不多去用到。這裏你們主要去理解與實踐屬性的基本使用與定義、Getter()Setter()、後期初始化屬性、以及編譯時常數這幾個點就能夠了。

源代碼

若是各位大佬看了以後感受還闊以,就請各位大佬隨便star一下,您的關注是我最大的動力。
個人我的博客Jetictors
GithubJteictors
掘金Jteictors

歡迎各位大佬進羣共同研究、探索

QQ羣號:497071402

相關文章
相關標籤/搜索