2017-01-05 4 views
0

私はいくつかのプライベートプロパティが必要なテストケースを書いています。これらのプライベートデータはプライベートメソッドから生成されるため、計算が完了した後にリフレクションを使用することにしました。後で私は代理プロパティを思い出して、一般的な代議員を書くことにしました。ここで私は今のところ得たコードは次のとおりです。イニシャライザでこのヌルチェックを安全に解決するにはどうすればよいですか?

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 
    } 
} 

あなたが見ることができるように、プロパティcacheinitOnceが設定されている場合は、getValueの呼び出しによって初期化され、後続の呼び出しではなく、高価な反射を呼び出す維持のキャッシュを使用します。

非常に不幸なことは、Tがnull可能な型であり、late initのメカニズムが壊れている可能性がありますが、私がnullで初期化してもTが非nullタイプとnull安全性が壊れています。

現在のところ、nullを返すJava関数の戻り値を使用して初期化することで、この問題を解決しました。私は生成されたバイトコードを検査し、kotlinコンパイラがnullチェックをしていないことを確認しましたが、現在は動作しますが、将来の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 
    } 
} 

答えて

3

一つの方法は、上限のみ非NULL可能タイプに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 
    } 
} 
+0

これまでのところ、これが最良の解決策です。もしnullとnullでない型の両方を望むなら、普通のJavaをハックしたり使う必要があります。 – glee8e

+0

@ glee8e Javaでできるコードがありません。コトリンではできません – voddan

+0

私の更新 – voddan

関連する問題