我創建了 2 個 kotlin 方法:一個用于檢查型別,另一個用于轉換物件。他們看起來像:
fun Any?.isOfType(type: Class<*>): Boolean{
return type.isInstance(this)
// return `this is T` does NOT work.
}
和
fun <T> Any?.castToType(): T {
return this as T
// Works, albeit with a warning.
}
我已經閱讀了一些關于泛型和擦除的帖子,但我無法克服似乎存在差異的問題。
為什么檢查物件的型別不能用泛型來完成,而是轉換為泛型可以?
uj5u.com熱心網友回復:
問題是為什么:
fun <T> Any?.castToType() = this as T // compiles with warning
"hello".castToType<Int>()
“作業”,但這甚至不會編譯:
fun <T> Any?.isOfType() = this is T // won't compile
"hello".isOfType<Int>()
實際上兩者都不起作用。在這兩種情況下,型別都會在運行時被擦除。那么為什么一個編譯而另一個不編譯呢?
this is T由于 T 的型別未知,因此無法在運行時作業,因此編譯器必須拒絕它。
this as T另一方面可能有效:
"hello".castToType<Int>() // no runtime error but NOP
"hello".castToType<Int>().minus(1) // throws ClassCastException
2.0.castToType<Int>().minus(1) // no runtime error, returns 1
在某些情況下它可以作業,在其他情況下它會引發例外。現在,每個未經檢查的強制轉換都可能成功或導致運行時例外(有或沒有泛型型別),因此顯示警告而不是編譯錯誤是有意義的。
概括
- 具有泛型型別的未經檢查的強制轉換與沒有泛型型別的未經檢查的強制轉換沒有什么不同,它們很危險,但警告就足夠了
- 另一方面,在運行時不可能使用泛型型別進行型別檢查
附錄
官方檔案解釋了型別擦除以及為什么使用型別引數的 is-checks 在運行時無法成功:
在運行時,泛型型別的實體不包含有關其實際型別引數的任何資訊。據說型別資訊已被擦除。例如,Foo 和 Foo<Baz?> 的實體被擦除為僅 Foo<*>。由于型別擦除,沒有通用的方法來檢查泛型型別的實體是否在運行時使用某些型別引數創建,并且編譯器禁止諸如 ints is List 或 list is T(型別引數)之類的 is-check
(https://kotlinlang.org/docs/generics.html#type-erasure)
用我自己的話來說:如果我不知道 B 是什么,我就無法檢查 A 是否是 B。如果 B 是一個類,我可以檢查該類的實體(這就是為什么type.isInstance(this)有效),但如果 B 是泛型型別,則運行時沒有關于它的資訊(它已被編譯器洗掉)。
uj5u.com熱心網友回復:
這不是關于鑄造與檢查;這是關于使用泛型與類物件。
第二個例子是通用的;它T用作型別引數。不幸的是,因為泛型是使用型別擦除實作的,這意味著該型別在運行時不可用(因為它已被擦除,并被相關的上限替換——Any?在這種情況下)。這就是為什么諸如型別檢查或轉換為型別引數之類的操作可能不安全并給出編譯警告的原因。
但是,第一個示例不使用型別引數。相反,它使用一個名為 type的引數,但它是一個Class物件,代表一個特定的類。這是在運行時提供的值,就像任何其他方法引數一樣,因此您可以呼叫諸如cast()和isInstance()之類的方法來處理運行時的某些型別問題。然而,它們與反射密切相關,并且具有一些相同的缺點,例如脆弱、丑陋的代碼和有限的編譯時檢查。
(Kotlin 代碼經常使用KClass物件而不是 JavaClass物件,但原理是一樣的。)
可能值得強調class和type之間的區別,它們相關但有細微的不同。例如,String既是類又是型別,String?而是從同一類派生的另一種型別。?LinkedList是一個類,但不是一個型別(因為它需要一個型別引數);LinkedList<Int>是一種型別。
型別當然可以從介面以及類派生,例如Runnable,或MutableList<Int>。
這與問題有關,因為泛型使用型別引數,而Class物件表示類。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/510469.html
標籤:科特林仿制药
