在下面,我有一個fun <T : Number> sum(list : List<T>) : T帶有型別引數的泛型函式T : Number。
在函式中,我將串列的數字sum : Double總結為 a并在最后將總和轉換為return sum as T。
例如,如果Int傳遞了一個串列,我也會回傳一個Int- 并且這是有效的。
fun <T : Number> sum(list : List<T>) : T {
var sum = 0.0
for(x in list)
sum = x.toDouble()
return sum as T
}
fun main() { println(sum(listOf(1,2,3))) } // prints 6
然而,以下不起作用,我想知道為什么上面的泛型函式起作用,但直接將 a 轉換Double為 anInt不起作用。
fun main() {
val d : Double = 6.0
val i = d as Int // java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
println(i)
}
我不得不承認,我預計這兩種情況都會失敗,但令人驚訝的是,通用函式可以作業,但我不知道為什么。
所以問題是:為什么泛型函式可以作業并且在從 Double 轉換為 Int 時不會拋出 ClassCastException?
uj5u.com熱心網友回復:
請注意,在第一個“有效”的代碼片段中,您實際上并未將結果轉換為Int. 如果您使用的是 IntelliJ,它應該將強制轉換標記為“未經檢查的強制轉換”。這意味著在運行時,不會檢查是否sum可以實際轉換為 type T。它只檢查那sum是 a Number,就是這樣。不執行任何其他操作。
您可以通過列印看到回傳的值仍然是 a Double,而不是 an Int:
println(sum(listOf<Int>(1,2,3)) is Int) // false
println(sum(listOf<Int>(1,2,3)) is Double) // true
正如其他答案所解釋的那樣,這是因為型別擦除。
你仍然看到6,但不是6.0更多的原因。Kotlin 編譯器看到sum這里的呼叫應該回傳 an Int(此時型別還沒有被擦除),所以它找到了println帶 an的多載Int,它行內到 JavaSystem.out.prinln(int)方法。要呼叫這個方法,編譯器必須生成轉換型別擦除碼Number是sum回傳到int,所以它呼叫Number.intValue。
因此,這是生成的內容:
33: invokestatic #69 // Method sum:(Ljava/util/List;)Ljava/lang/Number;
36: invokevirtual #73 // Method java/lang/Number.intValue:()I
39: invokevirtual #79 // Method java/io/PrintStream.println:(I)V
如果你強迫編譯器呼叫pritnln(Any),那么它會列印6.0:
val any: Any = sum(listOf<Int>(1,2,3))
println(any)
uj5u.com熱心網友回復:
這是因為型別擦除。在您的情況下,通用資訊僅在編譯期間可用。在運行時sum as T沒有注意到,因為不清楚 T 是什么。例如,不可能列印出 T 的型別。這是一個未經檢查的強制轉換。您也可以將T型別從Numberto更改為String,這沒有意義 - 但它會編譯。所以該函式不會 trow ,ClassCastException因為實際上它沒有強制轉換。如果將sum函式更改為精煉 type,它會在運行時保留型別資訊,它將執行強制轉換并拋出錯誤:
inline fun <reified T : Number> sum(list: List<T>): T {
var sum = 0.0
for (x in list)
sum = x.toDouble()
return sum as T // java.lang.ClassCastException
}
uj5u.com熱心網友回復:
它進行轉換,但轉換是泛型型別的上限,即 Number
您可以在代碼的反編譯版本中看到這種情況:
public static final Number sum(@NotNull List list) {
Intrinsics.checkNotNullParameter(list, "list");
double sum = 0.0D;
Number x;
for(Iterator var4 = list.iterator(); var4.hasNext(); sum = x.doubleValue()) {
x = (Number)var4.next();
}
return (Number)sum;
}
如果您查看該代碼,編譯器還會告訴您強制轉換T為多余的,因為這已經得到了保證。
相當于:
fun main() {
val d : Double = 6.0
val i = d as Number
println(i)
}
這也不會導致 ClassCast 例外。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/375642.html
