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

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

fun <T> reflect(instance: Any, initOnce: Boolean = true) = ReflectBackedProperty<T>(initOnce, instance) class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> { 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 ,则后续调用将使用该缓存而不是保持调用昂贵的反射。

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

目前,我通过使用返回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 <T> * @return */ public static <T> T NULL() { return null; } } 

ReflectBackedProperty.kt

 import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty fun <T> reflect(instance: Any, initOnce: Boolean = true) = ReflectBackedProperty<T>(initOnce, instance) class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> { 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限制为仅具有上限的非空类型:

 class ReflectBackedProperty<T: Any> : ReadOnlyProperty<Any, T> {} 

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

 class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> { 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 } }