這篇文章是一時興起想寫的,由於我發現我對Kotlin的屬性理解一直有誤java
首先咱們要搞清楚在 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翻譯一下:app
class Person {
var age = 0
}
複製代碼
能夠對比出,Kotlin裏不須要額外的get、set方法,固然你也寫不了,由於 Kotlin 已經默認實現了get、set,因此在Kotlin裏,咱們寫不出 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
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
複製代碼
很明顯,這裏就是直接生成了兩個靜態函數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 的理解