理解 Kotlin 中的屬性(property)

這篇文章是一時興起想寫的,由於我發現我對Kotlin的屬性理解一直有誤java

Java 中的屬性是什麼(property)

首先咱們要搞清楚在 Java 中屬性是什麼,在 Java 中類的屬性不是指一個字段,而是一個字段和它的get、set方法加在一塊兒纔算一個屬性,好比:android

class Person {
    int age;
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = age
    }
}
複製代碼

而若是不給這個 name 寫get、set方法,它就只是一個 field,能夠稱它爲 字段 或者 bash

Kotlin的類只有屬性(property)沒有獨立的字段(field)

若是上面的代碼用kotlin翻譯一下:app

class Person {
    var age = 0
}
複製代碼

能夠對比出,Kotlin裏不須要額外的get、set方法,固然你也寫不了,由於 Kotlin 已經默認實現了get、set,因此在Kotlin裏,咱們寫不出 field編輯器

Kotlin 中的 backing field 是什麼

前面說 Kotlin 中不能寫 field,可是咱們不少時候必需要用到 field,好比你複寫一個屬性的set方法函數

var age = 0
    set(value){
        age = value + 1
    }
複製代碼

若是這樣寫實際上是會發生遞歸,沒法賦值成功 ui

這裏AS也提醒你了,這裏發生了遞歸
因此咱們通常都這麼寫:

var age = 0
    set(value){
        field = value + 1
    }
複製代碼

這裏出現了一個 field 的東西能夠訪問和賦值,這個東西就是 Kotlin 的幕後字段(Backing Field)
咱們能夠簡單地理解爲,Kotlin 沒有明面上的 field,可是它存在於幕後this

沒有 backing field的成員是什麼?

class Person{
    private var _age =0
    var age:Int
        get() = _age
        set(value) {
            _age = value
        }
}
複製代碼

當咱們聲明一個 var 爲私有時,好比上面的_age,咱們叫它 幕後屬性 ,雖然它看起來不須要get、set方法,可是其實仍然是有的,只不過它的get、set方法都被聲明爲 private 了spa

固然咱們這裏不是想討論幕後屬性,而是要討論一下這個 age 是個什麼玩意,是一個成員屬性嗎,其實經過字節碼就能夠知道,這裏的Person類實際並不存在age這個成員,它只是幫你生成了對_age的兩個public final的get和set方法翻譯

// access flags 0x2
private I _age

// access flags 0x11
public final getAge()I
 L0
  LINENUMBER 28 L0
  ALOAD 0
複製代碼

和你本身直接寫一個

fun getAge() = _age
複製代碼

是如出一轍的

再舉一個例子

var View.topPadding: Int
    inline get() = paddingTop
    set(value) = setPadding(paddingLeft, value, paddingRight, paddingBottom)
複製代碼

這是從anko的拷出來一段代碼,經過這個擴展成員,咱們能夠直接對某個 View 的 PaddingTop 進行修改和讀取,雖說是成員,可是咱們把一段字節碼拿出來看一下:

// access flags 0x19
public final static getTopPadding(Landroid/view/View;)I
  @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0****
...
// access flags 0x19
public final static setTopPadding(Landroid/view/View;I)V
  @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
複製代碼

很明顯,這裏就是直接生成了兩個靜態函數getTopPaddingsetTopPadding,並非真的爲View這個類添加了一個成員,那這個東西到底什麼呢?咱們把上面的代碼稍微改一下:

var View.topPadding: Int = 0 
    inline get() = paddingTop
    set(value) = setPadding(paddingLeft, value, paddingRight, paddingBottom)
複製代碼

給這個成員加個默認值,能夠看到,編輯器報錯了

而且告訴你這個屬性沒有幕後字段,因此不能初始化,好吧,官方給出了定義, 這就是一個屬性(property)

總結

在Kotlin中一個 property 無論有沒有 backing field 都稱之爲 property,而在 Java 中 field + get、set方法一塊兒才能是一個 property。
若是咱們從Java 的角度去看一個沒有 backing field 的 property,能夠理解爲 Kotlin 對 以get、set開頭這樣的函數的語法糖,這種語法糖有什麼用呢?我的以爲是爲了DSL語法服務的,仍是以上面那個topPadding爲例,當你在用 DSL 語法設置一個view的時候,好比:

view.apply {
    background = getDrawable(R.drawable.bg)
    visibility = View.INVISIBLE
    ...
}
複製代碼

前面都是一個屬性等於一個值,這個時候下面跟上 topPadding = xxx,語義十分清晰連貫,若是這裏忽然用一個 setTopPadding(this,xxx) ,不只代碼不美觀,並且打斷了閱讀代碼和編寫代碼的人的思惟上的連貫性。

以上就是我對 Kotlin 的 Property 的理解

相關文章
相關標籤/搜索