簡體   English   中英

Kotlin reified類型參數不能用作函數體中的類型參數

[英]Kotlin reified type parameter can't be used as type parameter in body of function

Kotlin中的reified類型參數可防止類型參數擦除,並允許在運行時知道type參數。 這允許以下代碼按預期編譯和運行:

inline fun <reified T> isA(value: Any) = value is T

但是,當我嘗試使用“T”作為類型參數而不是獨立時,我得到一條消息,它是一個擦除類型。 以下代碼證明了這一點,僅用於說明目的

inline fun <reified T> isListOfA(name: String): Boolean {
    val candidate = Class.forName(name)
    return candidate is List<T>
}

這是由於技術限制嗎? 如果是這樣,那個限制是什么?

顯然我沒有恰當地提出我的問題來得到我想要的形式的答案。 這里的大多數答案都是“因為你不能用Java做的”的一些變體。 好吧,你不能在Java中使用x instanceof T ,但你可以在Kotlin中做x is T 我正在尋找潛在的實際障礙,而不是Java規則。 畢竟,規則被打破了。

從我對這里第一個答案的評論,重新提出的問題是:如果objectref is T可以通過某種機制X在Kotlin中工作,為什么不能使用objectref is SomeClass<T>通過相同的機制工作?

tl; dr回答:因為在運行時沒有SomeClass<T> Class對象。

更長的答案:首先我們必須了解機制Xis T生成字節碼指令的instanceof 該指令采用objectref和某些類C的名稱N ,其中N由編譯器根據上下文確定。 在運行時,從N派生的類C將用於評估objectref is T表達式。 為了進行此評估,必須實例化C的類對象。 因此,對於objectref is SomeClass<T>使用相同的機制objectref is SomeClass<T>那么N將是SomeClass<T> 由於類型擦除, SomeClass<T>不會有類對象,因此無法生成所需的instanceof指令,從而應用相同的機制。 此外, instanceof指令不能采用SomeClass<T>形式的名稱。 因此,如果objectref is SomeClass<T>要工作,則必須在Kotlin中找到並實現其他一些機制Y 這種機制可能存在也可能不存在。

我知道有些人可能會說這與其他一些答案是一回事。 然而,無論好壞,我的學習方式是了解事物如何在金屬上起作用,然后將其與抽象模型相結合。 在這種情況下,Java Generics擦除概念是抽象模型(或其一部分)。 真的,“擦除”對我來說感覺很軟弱,除非我至少了解它在工作實現中實現的一種方式。

阻止您這樣做的技術限制是JVM上的泛型類型擦除 基本上,在運行時,泛型類型List<T>對象只是一個與對象一起工作的List :它只在編譯時才檢查類型安全性的賦值和函數調用。 實際類型參數T僅在編譯期間存在,然后被擦除。 它無法在運行時恢復(至少目前為止:有一天Project Valhalla可能會為JVM引入運行時驗證的泛型)。

在非內聯Kotlin函數(以及非reified類型參數)中,您甚至無法進行第一種檢查, value is T ,因為普通類型參數也會被刪除。

使用reified類型參數,函數體在其調用站點內聯,實際(或推斷)類型參數替換為T :當您調用isA<String>("abc") ,調用站點將具有字節碼instanceof檢查String

但是即使使用了reified類型參數,也無法對內部類型進行內省:您可以檢查something is List<*>但不是something is List<String> :類型參數不會存儲在運行時的任何位置。

另請注意isA<List<String>>(listOf(1, 2, 3))將返回true 這就是Kotlin處理這個奇怪的情況:只有該類型的非泛型部分可以在運行時實際檢查,所以它是。

因為Java在編譯時將泛型類型參數T擦除為Object / upper-bounded類型,所以在Kotlin中無法做到這一點。

第一種方法可以工作,因為value is T被內聯到具有reified類型的call-site函數,例如:

//val is_string = isA<String>(1) // inline into the call-site function as below:


val i:Int = 1
//                   v--- the actual type argument is inlined here
val is_string = 1 is String

參數化類型總是在運行時擦除。 因此,您可以檢查值是否是T實例但不是T<V>實例,無論TV是否已實現或硬編碼。

但是,即使可能,您的示例代碼也沒有意義,因為它檢查具有該名稱的類型是否是List的實例 ,而不是檢查具有該名稱的類型是否是預期的List類型。

如果你有一個對象的實例 ,並想要檢查它是否只包含期望類型的項目,你仍然可以這樣寫:

inline fun <reified T> isListOfA(instance: Any)
    = instance is List<*> && instance.all { it is T }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM