[英]Why can't type parameter in Kotlin have any other bounds if it's bounded by another type parameter?
[英]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
對象。
更長的答案:首先我們必須了解機制X
, is 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>
實例,無論T
和V
是否已實現或硬編碼。
但是,即使可能,您的示例代碼也沒有意義,因為它檢查具有該名稱的類型是否是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.