这篇文章是一时兴起想写的,因为我发现我对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 的理解