Property(属性)
在说BackingProperty
之前要先知道什么Property
。
先考虑一个问题,我们经常说的Field
,Property
,Variable
到底各自指代什么?
Glossary of Terms (oracle.com)
其实是可以在Oracle官网上找到答案的:
- Field :类的数据元素,除非特别指明,否则
field
是非静态的
- Property : 可设置的一个对象的特点(feature)
- Variable : 不特别与实例相关的数据元素
通过上面的描述可以看出,其实根据与类实例的相关性,可以将Variable
单独考虑。
其余两种则可以通过是对外提供操作来区分Field(不对外提供操作)
和Property(对外提供操作)
BackingProperty
幕后属性,想要理解什么是幕后属性需要先理解在Kotlin中是如何对属性进行操作的。
举个例子:
1 2 3 4 5 6 7
| class Example { inner class Inner(var x: Int)
fun test() { println(Inner(1).x) } }
|
这段栗子看起来很简单,一个内部类有一个Int属性,在test方法中打印了innner对象的x。
下面把这段看起来很简洁的代码反编译成Java看一看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public final class Example { public final void test() { int var1 = (new Example.Inner(1)).getX(); System.out.println(var1); }
public final class Inner { private int x;
public final int getX() { return this.x; }
public final void setX(int var1) { this.x = var1; }
public Inner(int x) { this.x = x; } } }
|
现在理解了属性,那backing
应该怎么理解呢?看看下面的栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Example { inner class Inner { private var y: Int = 0 var x: Int get() = x set(value) { y = x } }
fun test() { println(Inner().x) } }
|
可以看到,在这里对于属性x
的操作被转移到了一个私有成员y
上,get()
方法实际上取值是y
而set()
方法则直接将value
赋值给了y
。来看一看对应的java代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public final class Example { public final void test() { int var1 = (new Example.Inner()).getX(); System.out.println(var1); }
public final class Inner { private int y; public final int getX() { return this.y; }
public final void setX(int value) { this.y = value; } } }
|
我们知道对于属性来说,Kotlin会将属性的直接访问映射成对应的get()
和set()
方法,而在这个例子中我们可以看出,这两个方法实际上操作的是类成员y
。
这其实就是BackingProperty
了。意义就是get()
和set()
方法真正操作的背后的那个Field
。
那可能有人会问了,如果Kotlin中get()
和set()
方法就操作同名的成员呢?
还是用代码来说:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Example { inner class Inner { var x: Int get() = x set(value) { x = value } }
fun test() { println(Inner().x) } }
|
代码很简单,看上去好像没什么问题,反编译成Java代码看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public final class Example { public final void test() { int var1 = (new Example.Inner()).getX(); System.out.println(var1); }
public final class Inner { public final int getX() { return this.getX(); }
public final void setX(int value) { this.setX(value); } } }
|
一眼就看出问题了,这咋还出递归了呢?
再强调一下,Kotlin中对Property的调用会被影射成对应的get()
和set()
方法,所以在上面的Kotlin代码中,直接去调用x
是不对的!
正确的写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Example { inner class Inner { var x: Int = 0 get() = field set(value) { field = value } }
fun test() { println(Inner().x) } }
|
编译成Java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public final class Example { public final void test() { int var1 = (new Example.Inner()).getX(); System.out.println(var1); }
public final class Inner { private int x;
public final int getX() { return this.x; }
public final void setX(int value) { this.x = value; } } }
|
到这里还剩最后一个问题:为什么Kotlin中加了默认值,到Java里就没了?
不卖关子,其实还是有的,只不过如果一个int
型作为类成员,那么在类初始化的时候会被初始化为默认值0。所以在这个地方就被省略了,也不需要再赋值一次。如果Kotlin中的默认值为非0数,则对应的Java代码中会带有一个显式的初始化。
Over.