2016-03-27 573 views
14

使用reified type parameters,可以寫一個內聯函數與類型參數通過反射在運行時的工作原理:如何獲取Kotlin中實際泛型參數的實際類型參數?

inline fun <reified T: Any> f() { 
    val clazz = T::class 
    // ... 
} 

但當f是帶一個參數,它本身是一個通用類,似乎是沒辦法通過T::class獲得其實際類型參數:

f<List<Integer>>() // T::class is just kotlin.collections.List 

有沒有辦法通過反射得到具體化通用的實際類型參數?

+0

更換** **任何與所需的接口,並與工作接口:) –

回答

22

由於type erasure,實際泛型參數不能通過通用類的T::class標記獲得。類的不同對象必須具有相同的類標記,這就是爲什麼它不能包含實際的泛型參數。

但是有一個叫做super type tokens的技巧,它可以在編譯時知道類型的實際類型參數(因爲內聯對Kotlin中的泛化泛型是真的)。

訣竅是編譯器保留了從泛型類派生的非泛型類的實際類型參數(它的所有實例都有相同的參數,很好的解釋here)。他們可以通過實例的clazz.genericSuperClass.actualTypeArguments訪問。

鑑於這一切,你可以寫一個實用程序類是這樣的:

abstract class TypeReference<T> : Comparable<TypeReference<T>> { 
    val type: Type = 
     (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] 

    override fun compareTo(other: TypeReference<T>) = 0 
} 

解釋Jackson TypeReference它使用相同的方法。傑克遜Kotlin模塊uses it關於泛化的泛型。

之後,在與通用的具體化,需要一個TypeReference被子類的內聯函數(一個object expression會去),然後將其type可以使用。

實施例:

inline fun <reified T: Any> printGenerics() { 
    val type = object : TypeReference<T>() {}.type 
    if (type is ParameterizedType) 
     type.actualTypeArguments.forEach { println(it.typeName) } 
} 

printGenerics<HashMap<Int, List<String>>>()

java.lang.Integer 
java.util.List<? extends java.lang.String>