理解 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 的理解

相关文章
相关标签/搜索