实用 反射 实现 引用的引用


#1

最近在搞代理“by”,发现代理看似方便,但实际不好拿来用,因为代理的格式很繁琐
Class A{

var b:B = B()

//var v:String by b.v //我希望这里能直接去用 b 的成员 v,但是不能这么写

//可以这么写,但太过繁琐
var v: String
    set(value) {
        b.v = value
    }
    get() = b.v

//通过代理类 VarValue 去代理使用 b.v,但问题是一旦 b.v 被改变,比如 b.v = null ,代理类就会持有已经没用的 v。
//var v:String by VarValue(b.v)

//var v:String by VarValue(b::v) //使用反射实现对 b.v 的追踪,即使 b.v 被改变,也能拿到改变后的数据

}

class VarValue(var b: KMutableProperty0) {
operator fun getValue(a: A, property: KProperty<*>): T {
return b.get()
}

operator fun setValue(a: A, property: KProperty<*>, any: T) {
	b.set(any)
}

}
//写完这个类,我就有个担心,Kotlin 的反射耗时多吗?于是我测了测

@Test //反射性能测试
fun fieldTimeTest() {
val range = 0…10_000_000
val demoEE = DemoEE()

	val l = measureTimeMillis {
		for (i in range) {
			val x1 = demoEE::x1
			x1.set(i.toString())
		}
	}
	println("K反射:$l")

	val l2 = measureTimeMillis {
		for (i in range) {
			demoEE.x1 = i.toString()
		}
	}
	println("普通:$l2")

	val l3 = measureTimeMillis {
		for (i in range) {
			val x1 = demoEE.javaClass.getDeclaredField("x1")
			x1.isAccessible = true
			x1.set(demoEE, i.toString())
		}
	}
	println("J反射:$l3")
}

–第一次测试
K反射:291
普通:291
J反射:949
–第二次测试
K反射:285
普通:275
J反射:943
–第三次测试
K反射:287
普通:263
J反射:986
// 由测试结果可见,完全不必对耗时担心。

但它内部是怎么做的,会不会造成内存泄漏呢?
通过 Studio 自带的反编译工具 - show Kotlin Bytecode。看到Kotlin 生成了这么个类
final class DemoEE$demo$3 extends MutablePropertyReference0 {
DemoEE$demo$3(DemoEE var1) {
super(var1);
}

@Nullable
public Object get() {
return ((DemoEE)this.receiver).getX();
}

public void set(@Nullable Object value) {
((DemoEE)this.receiver).setX(((Number)value).intValue());
}
}
就只是包裹了一层而已呀,根本没用到反射,怪不得这么快

综上所述,这种代理是可行的。


#2

所以你想说什么?
如果一个对象的属性代理另一个对象的属性,可以试下 https://github.com/enbandari/ObjectPropertyDelegate


#3

就是你说的这个代理属性,让 A.v 代理 B.v,实现任何对 A.v 的操作都作用在 B.v 上,就像拿着的引用不是 A.v 而是 B.v 的引用一样。:+1:


#4

好的~属性代理其实不涉及反射,一般就是字节码生成的。你的这个需求场景可以稍微封装一下,里面会涉及反射的一些api调用。


京ICP备16022265号-2 Kotlin China 2017 - 2018