委托那点事

前言

  本来这篇文章只是想写写kotlin中的lateinit和by lazy之间的区别,但是发现内容太少了,于是乎就想着顺便将我自己对于委托的理解写出来。

属性

  kotlin中的属性,天生就带有setter()/getter()方法,比如我们在某个类中定义一个属性

1
var name:String = "fxyan"

  实际上它和下面的代码是等价的。

1
2
3
4
5
var name:String = "fxyan"
get() = field
set(value){
field = value
}

  起初我看到这个的时候很纳闷,这个field是什么,其实field就是代表了这个域本身。我们可以通过AndroidStudio的kotlin bytecode工具将字节码反编译成Java文件来揭露它神秘的面纱。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class TestClass {
@NotNull
private String name = "";

@NotNull
public final String getName() {
return this.name;
}

public final void setName(@NotNull String value) {
Intrinsics.checkParameterIsNotNull(value, "value");
this.name = value;
}
}

lateinit

  我们知道,如果一个类里面的某个属性定义成lateinit之后,在使用的时候如果这个属性没有被初始化,就会抛异常,通过上面对属性的讲解,我们不难推出lateinit关键字做了什么事情。

1
lateinit var name

  我们可以看到一个有趣的现象,lateinit关键字修饰的属性不能够自定义getter()/setter()方法。001
  其实lateinit关键字无非就是重写了name属性的getter()/setter()方法,在里面做了空值的判断,如果为空则抛出异常。这点我们同样可以通过kotlin bytecode工具来查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public final class TestClass {
@NotNull
public String name;

@NotNull
public final String getName() {
String var10000 = this.name;
if (this.name == null) {
Intrinsics.throwUninitializedPropertyAccessException("name");
}

return var10000;
}

public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
}

  所以我们可以很明显的知道一点,lateinit关键字不能修饰在可空的属性上,因为setter()方法中会校验,如果为空,则会抛出异常

委托

  下面我们来看看kotlin中的委托

类委托

  类委托就是类中定义的方法实际上调用另外一个类的对象的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
interface IBase {
fun print()
}

class IBaseImpl(
var name: String
) : IBase {
override fun print() {
print(name)
}
}

class Delegate(iBase: IBase) : IBase by iBase

  我们执行下面的代码。

1
2
3
4
fun main(args:Array<String>){
val base:IBase = IBaseImpl("fxyan")
Delegate(base).print()
}

  得到了如下的运行结果。002
  通过运行结果我们大致上也能猜到它是怎么实现的。下面的代码不是通过反编译出来的,但是反编译出来的和下面的差不多。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class DelegateTest {

public static void main(String[] args) {
IBase iBase = new IBaseImpl("fxyan");
new Delegate(iBase).print();
}

}

interface IBase {
void print();
}

class IBaseImpl implements IBase {
private String name;

public IBaseImpl(String name) {
this.name = name;
}

@Override
public void print() {
System.out.print(this.name);
}
}

class Delegate implements IBase {

private IBase iBase;

public Delegate(IBase iBase) {
this.iBase = iBase;
}

@Override
public void print() {
iBase.print();
}
}

属性委托

  定义一个委托属性很简单,只需要通过by关键字,by关键字后面就是属性委托。

1
var/val <property name>: <Type> by <expression>

  首先我们先定义一个委托类Delegate

1
2
3
class Delegate{

}

  然后定义一个类拥有一个委托属性。

1
2
3
class DelegateProperty{
var property:String by Deletage()
}

  编译器报错了003,我们需要给Delegate类增加getValue(),setValue()方法。

1
2
3
4
5
6
7
8
9
10
class Delegate {

fun getValue(d: DelegateProperty, p: KProperty<*>): String {
return ""
}

fun setValue(d: DelegateProperty, p: KProperty<*>, newValue: String) {
}

}

  这个时候编译器仍然在报错004Delegate类的getValue(),setValue()方法需要添加operator关键字。

1
2
3
4
5
6
7
8
9
10
11
class Delegate {

operator fun getValue(d: DelegateProperty, p: KProperty<*>): String {
return "$d 对象的${p.name}属性的getter()被委托给Delegate对象的getValue()"
}

operator fun setValue(d: DelegateProperty, p: KProperty<*>, newValue: String) {
println("$d 对象的${p.name}属性的setter()被委托给Delegate对象的setValue()")
}

}

  这样一个完整的委托类就编写完了,下面我们来进行一下测试。

1
2
3
4
5
6
7
fun main(args: Array<String>) {
val d = DelegateProperty()
println(d.property)

d.property = "fxyan"
print(d.property)
}

  可以得到如下的运行结果005。通过上面一系列的介绍,我们大致上可以推断出,属性委托实际上就是将属性的getter()/setter()方法委托给委托类的getValue()/setValue()。这点我们通过kotlin bytecode工具得到了证实。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class DelegateProperty {
@NotNull
private final Delegate property$delegate = new Delegate();

static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(DelegateProperty.class), "property", "getProperty()Ljava/lang/String;"))};

@NotNull
public final String getProperty() {
return this.property$delegate.getValue(this, $$delegatedProperties[0]);
}

public final void setProperty(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.property$delegate.setValue(this, $$delegatedProperties[0], var1);
}
}

lazy属性

  lazy实际上是一个扩展函数,接受一个lambda表达式作为参数

1
public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

  同时我们看看SynchronizedLazyImpl()这个类。

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
26
27
28
29
30
31
32
33
34
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE

private val lock = lock ?: this

override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}

return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
}
else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}

override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE

override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."

private fun writeReplace(): Any = InitializedLazyImpl(value)
}

  我猜想,lazy实际上是在使用的时候调用这个类的get()方法,问了证实我的猜想,我在get()方法中断点了,同时我编写了如下几个类。

1
2
3
4
5
6
7
8
9
10
11
fun main(args: Array<String>) {
val d = Delegate()
println(d.name)
println(d.name)
}

class Delegate {
val name: String by lazy {
""
}
}

  当执行val d = Delegate()这句话的时候并没有调用get()方法,当走到第二句话访问name属性的时候,走到了SynchronizedLazyImpl()类的get()方法,从SynchronizedLazyImpl类中,我们知道,第一次访问name属性的时候,value是一个单例类的对象private object UNINITIALIZED_VALUE。在get()方法中会走下面的分支,并使用lazy传入的lambda表达式赋值给value。

1
2
3
4
5
6
7
8
9
10
11
12
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
}
else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}

  而且我们发现它使用了同步,在多线程中使用lazy也不会发生问题,这一点是值得称赞的。当第二次访问的时候value已经被lambda表达式赋值,所以会走前面的分支。

1
2
3
4
5
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}

  至此我们总结下lazy的使用
  1. lazy只能修饰val的变量;
  2. lazy是线程安全的;
  3. 创建对象时,lazy修饰的域不会被初始化,只有第一次访问域的时候会使用lazy后面的lambda表达式进行初始化,之后再访问则直接返回初始化后的值。

Observable属性

  这玩意实际上就一观察者,有点类似于Android新出的arch库中的LiveData,但是又没有其功能强大(斜眼笑)。通过Delegate.observable()我们可以创建一个可被观察的属性。

1
2
3
4
5
6
class ObservableDelegate {
var name: String by Delegates.observable("") {
_, oldValue, newValue ->
println("oldValue is $oldValue, newValue is $newValue")
}
}

  很好理解,每当name属性的值发生变化的时候,都会执行lambda表达式。我们来看看这个方法做了什么事情。

1
2
3
4
public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
}

  它接受一个初始值,和一个lambda表达式。然后我们看看ObservableProperty

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
private var value = initialValue

protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true

protected open fun afterChange (property: KProperty<*>, oldValue: T, newValue: T): Unit {}

public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value
}

public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val oldValue = this.value
if (!beforeChange(property, oldValue, value)) {
return
}
this.value = value
afterChange(property, oldValue, value)
}
}

  每当给name属性赋值的时候都会调到这个类的setValue(),在里面检测是否发生了变化,如果变化了就会调用afterChange(),而这个方法在Delegate.observable()里面的实现就是直接调用我们传入的lambda表达式。
  
  
  
  余生没那么长,不要一味的付出去惯那些得寸进尺的人,请忠于自己,活的像最初的模样~

本文标题:委托那点事

文章作者:严方雄

发布时间:2018-04-13

最后更新:2018-09-13

原始链接:http://yanfangxiong.com/2018/04/13/委托那点事/

0%