這篇文章是一時興起想寫的,因為我發現我對Kotlin的屬性理解一直有誤
Java 中的屬性是什麼(property)
首先我們要搞清楚在 Java 中屬性是什麼,在 Java 中類的屬性不是指一個欄位,而是一個欄位和它的get、set方法加在一起才算一個屬性,比如:
class Person {
int age;
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age
}
}
複製程式碼
而如果不給這個 name 寫get、set方法,它就只是一個 field,可以稱它為 欄位 或者 域。
Kotlin的類只有屬性(property)沒有獨立的欄位(field)
如果上面的程式碼用kotlin翻譯一下:
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
}
複製程式碼
如果這樣寫其實是會發生遞迴,無法賦值成功
這裡AS也提醒你了,這裡發生了遞迴所以我們一般都這麼寫:
var age = 0
set(value){
field = value + 1
}
複製程式碼
這裡出現了一個 field 的東西可以訪問和賦值,這個東西就是 Kotlin 的幕後欄位(Backing Field)
我們可以簡單地理解為,Kotlin 沒有明面上的 field,但是它存在於幕後
沒有 backing field的成員是什麼?
class Person{
private var _age =0
var age:Int
get() = _age
set(value) {
_age = value
}
}
複製程式碼
當我們宣告一個 var 為私有時,比如上面的_age,我們叫它 幕後屬性 ,雖然它看起來不需要get、set方法,但是其實仍然是有的,只不過它的get、set方法都被宣告為 private 了
當然我們這裡不是想討論幕後屬性,而是要討論一下這個 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
複製程式碼
很明顯,這裡就是直接生成了兩個靜態函式getTopPadding
和setTopPadding
,並不是真的為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 的理解