如何安全地解决这个空初始化器检查?

我正在写一个测试用例,我需要一些私有属性。 由于这些私人数据是从私人方法生成的,我决定在计算完成后使用reflection来检索它们。 后来我想起了委托的财产,并决定写一个总代表。 这是我到目前为止的代码:

fun  reflect(instance: Any, initOnce: Boolean = true) = ReflectBackedProperty(initOnce, instance) class ReflectBackedProperty(val initOnce: Boolean, val instance: Any): ReadOnlyProperty { var initialized = false lateinit var cache: T // <--- (a) override opertaor fun getValue(thisRef: Any, property: KProperty): Any? { @Suppress("UNCHECKED_CAST") if (!initialized || !initOnce) { cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T initialized = true } return cache } } 

如您所见,属性cachegetValue调用初始化,并且如果设置了initOnce ,则后续调用将使用该缓存而不是保持调用昂贵的reflection。

非常不幸的是,在(a)编译器抱怨,因为T可能是一个可为空的types,晚期的初始化机制将被打破,但如果我初始化它为null ,它仍然抱怨,因为T可能是一个非nulltypes,安全被打破。

目前,我通过使用返回null的Java函数的返回值来初始化它。 我检查了生成的字节码,发现kotlin编译器没有对它进行空的检查,所以现在可以工作,但是我担心未来的kotlin版本会有这样的检查并且毁掉这个技巧。 我应该如何克服这一点?


现在我正在使用这个,下面的代码被释放到公共领域。 如果你喜欢,可以提到这个页面,或者什么也不做。

KTHacks.java

 public final class KTHacks { private KTHacks() { throw new UnsupportedOperationException(); } /** * Forge a null into a platform type and take advantage of relaxed null-checks. * @param  * @return */ public static  T NULL() { return null; } } 

ReflectBackedProperty.kt

 import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty fun  reflect(instance: Any, initOnce: Boolean = true) = ReflectBackedProperty(initOnce, instance) class ReflectBackedProperty(val initOnce: Boolean, val instance: Any): ReadOnlyProperty { var initialized = false var cache: T = KTHacks.NULL() override operator fun getValue(thisRef: Any, property: KProperty): T { @Suppress("UNCHECKED_CAST") if (!initialized || !initOnce) { cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T initialized = true } return cache } } 

一种方法是将T限制为仅具有上限的非空types:

 class ReflectBackedProperty : ReadOnlyProperty {} 

另一种方法是不打扰lateinit的:

 class ReflectBackedProperty(val initOnce: Boolean, val instance: Any): ReadOnlyProperty { var initialized = false var cache: T? = null override operator fun getValue(thisRef: Any, property: KProperty<*>): T { if (!initialized || !initOnce) { cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T initialized = true } return cache as T } }