前言
本来这篇文章只是想写写kotlin中的lateinit和by lazy之间的区别,但是发现内容太少了,于是乎就想着顺便将我自己对于委托的理解写出来。
属性
kotlin中的属性,天生就带有setter()/getter()方法,比如我们在某个类中定义一个属性1
var name:String = "fxyan"
实际上它和下面的代码是等价的。1
2
3
4
5var 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
14public final class TestClass {
private String name = "";
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()方法。
其实lateinit关键字无非就是重写了name属性的getter()/setter()方法,在里面做了空值的判断,如果为空则抛出异常。这点我们同样可以通过kotlin bytecode工具来查看。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public final class TestClass {
public String name;
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
13interface IBase {
fun print()
}
class IBaseImpl(
var name: String
) : IBase {
override fun print() {
print(name)
}
}
class Delegate(iBase: IBase) : IBase by iBase
我们执行下面的代码。1
2
3
4fun main(args:Array<String>){
val base:IBase = IBaseImpl("fxyan")
Delegate(base).print()
}
得到了如下的运行结果。
通过运行结果我们大致上也能猜到它是怎么实现的。下面的代码不是通过反编译出来的,但是反编译出来的和下面的差不多。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
39public 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;
}
public void print() {
System.out.print(this.name);
}
}
class Delegate implements IBase {
private IBase iBase;
public Delegate(IBase iBase) {
this.iBase = iBase;
}
public void print() {
iBase.print();
}
}
属性委托
定义一个委托属性很简单,只需要通过by关键字,by关键字后面就是属性委托。1
var/val <property name>: <Type> by <expression>
首先我们先定义一个委托类Delegate1
2
3class Delegate{
}
然后定义一个类拥有一个委托属性。1
2
3class DelegateProperty{
var property:String by Deletage()
}
编译器报错了,我们需要给Delegate类增加getValue(),setValue()方法。1
2
3
4
5
6
7
8
9
10class Delegate {
fun getValue(d: DelegateProperty, p: KProperty<*>): String {
return ""
}
fun setValue(d: DelegateProperty, p: KProperty<*>, newValue: String) {
}
}
这个时候编译器仍然在报错Delegate类的getValue(),setValue()方法需要添加operator关键字。1
2
3
4
5
6
7
8
9
10
11class 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
7fun main(args: Array<String>) {
val d = DelegateProperty()
println(d.property)
d.property = "fxyan"
print(d.property)
}
可以得到如下的运行结果。通过上面一系列的介绍,我们大致上可以推断出,属性委托实际上就是将属性的getter()/setter()方法委托给委托类的getValue()/setValue()。这点我们通过kotlin bytecode工具得到了证实。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public final class DelegateProperty {
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;"))};
public final String getProperty() {
return this.property$delegate.getValue(this, $$delegatedProperties[0]);
}
public final void setProperty( 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
34private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
private var _value: Any? = UNINITIALIZED_VALUE
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
as T) (_v2
}
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
11fun 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
12return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
as T) (_v2
}
else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
而且我们发现它使用了同步,在多线程中使用lazy也不会发生问题,这一点是值得称赞的。当第二次访问的时候value已经被lambda表达式赋值,所以会走前面的分支。1
2
3
4
5val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
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
6class ObservableDelegate {
var name: String by Delegates.observable("") {
_, oldValue, newValue ->
println("oldValue is $oldValue, newValue is $newValue")
}
}
很好理解,每当name属性的值发生变化的时候,都会执行lambda表达式。我们来看看这个方法做了什么事情。1
2
3
4public 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表达式。然后我们看看ObservableProperty1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public 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表达式。
余生没那么长,不要一味的付出去惯那些得寸进尺的人,请忠于自己,活的像最初的模样~