2017-07-18 199 views
2

Kotlin中的實體類型參數可防止類型參數擦除,並允許在運行時知道類型參數。這讓下面的代碼編譯和運行按預期:Kotlin實體類型參數不能用作函數體中的類型參數

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

這是由於技術上的限制?如果是這樣,那麼這個限制是什麼?

+0

這不是物化的問題。你甚至不能'列舉''。 –

回答

0

顯然我沒有制定我的問題得到了我想要的表格的答案。這裏的大多數答案都是「因爲你不能用Java來做」的一些變化。那麼,你不能在Java中使用x instanceof T,但是你可以在Kotlin中使用x is T。我正在尋找潛在的實用障礙,而不是Java規則。畢竟,規則是被打破的。

從我就在這裏第一個答案評論,再形成的問題是:如果objectref is T可以做一些機制X爲什麼不能objectref is SomeClass<T>的是由相同的工作機制在科特林工作?

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

較長的回答:首先我們必須瞭解機制X,它是爲is T生成instanceof字節碼指令。該指令採用objectrefC某個類別的名稱N,其中N由編譯器根據上下文確定。在運行時,來自N的類C將用於評估objectref is T表達式。爲了進行此評估,必須實例化C的類對象。所以要使用objectref is SomeClass<T>這個相同的機制,那麼N就是SomeClass<T>。由於類型擦除,SomeClass<T>將不會有類對象,因此不可能生成所需的instanceof指令,從而應用相同的機制。另外,instanceof指令不能採用SomeClass<T>的形式。因此,如果要使用objectref is SomeClass<T>,則必須在Kotlin中找到並實施一些其他機制Y。這種機制可能存在也可能不存在。

我知道有些人可能會說這與其他一些答案是一樣的。然而,無論好壞,我的學習風格都是要理解事物在金屬上的運作方式,然後將其與抽象模型進行綜合。在這種情況下,Java泛型刪除的概念是抽象模型(或其中的一部分)。真的,「刪除」對我來說會覺得很糟糕,除非我至少明白在實施過程中實現的一種方式。

1

由於Java在編譯時將通用類型參數T擦除爲Object /上限類型,因此在Kotlin中無法執行此操作。

第一種方法能夠工作是因爲value is T內聯到調用點函數與具體化的類型,例如:

//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 
+0

是的,我得到的實際類型參數將被內聯爲「是T」爲「是字符串」。如果這是可能的,爲什麼不可能將其內聯爲「列表」,例如, 「列表」?這是我問的問題。 – roobyroo

+0

@roobyroo不,你不能,由於**類型擦除**發生在編譯時,所以java不支持** reified **泛型類型。 –

2

阻止你這樣做的技術限制是generics type erasure on JVM。基本上,在運行時,通用類型List<T>的對象變成僅適用於對象的List:只有在編譯時,纔會檢查類型安全性以進行賦值和函數調用。實際的類型參數T僅在編譯期間存在,然後被擦除。它不能在運行時恢復(至少目前爲止:有Project Valhalla可能會在一天內爲JVM引入運行時實現的泛型)。

在非內聯Kotlin函數(以及非特定類型參數)中,甚至不能執行第一種檢查value is T,因爲普通類型參數也會被擦除。

有了具體化類型參數,函數體被內聯在其調用點,與實際(或推斷)類型參數取代T:當你打電話isA<String>("abc"),呼叫網站將與instanceof檢查String字節碼。

但即使使用通用類型參數,也不能反思泛型:您可以檢查something is List<*>但不是something is List<String>:type參數不存儲在運行時的任何位置。

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

0

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

然而,即使這是可能的,你的示例代碼是沒有意義的,因爲它會檢查是否有該名稱的類型,而不是檢查的的實例列表的,如果與該名稱的類型是預期列表類型。

如果你有一個對象的實例並要檢查它只是將包含預期類型的​​項目的列表,你仍然可以寫這樣的事:

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

注意:這個'isListOfA'檢查可以咬人:考慮一個'MutableList '的只讀視圖,它在某個點只包含'String',因此通過'isListOfA (list)'檢查, '被添加,而您已經將列表傳遞給了一些代碼,該代碼期望所有項目都是'String's,因此失敗並帶有'ClassCastException'。 – hotkey

相關問題