引言:
JVM
上的泛型通常是經過類型擦除實現的,就是說泛型類實例的類型實參在運行時是不保留的。java
和Java同樣,Kotlin的泛型在運行時也被擦除了。這意味着泛型實例不會攜帶用於建立它的類型實參的信息。markdown
檢查一個值是否在列表,而不是set或者其餘對象的方式:可使用特殊的星號
投影語法來作這種檢查:函數
if (value is List<*>) { ... }
複製代碼
實際上,泛型類型擁有的每一個類型形參都須要一個***
[擁有未知類型實參的泛型類型**(或者類比於Java的List<?>
)]。ui
fun printSum(c: Collection<*>) {
// 這裏會有警告。Unchecked cast: List<*> to List<Int>
val intList = c as? List<Int>
?: throw IllegalArgumentException("List is expected")
println(intList.sum())
}
fun printSumTest() {
// 一切都符合預期要求
printSum(listOf(1, 2, 3))
// Set不是列表,因此拋出了異常
printSum(setOf(1, 2, 3))
// 類型轉換成功,但後面拋出了另外的異常
printSum(listOf("a", "b", "c"))
}
// 輸出結果
6
java.lang.IllegalArgumentException: List is expected
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number
複製代碼
由於編譯器沒有辦法判斷實參listOf("a", "b", "c")
是否是一個List,於是類型轉換會成功,可是iniList.sum()
調用時把String
當Number
用的嘗試會致使運行時的ClassCastException
。this
注意:Kotlin編譯器是足夠智能的,在
編譯期
它已經知道相應的類型信息
時,is
檢查是容許的。spa
fun printSum(c: Collection<Int>) {
// 此次檢查是合法的
if (c is List<Int>) {
println(c.sum())
}
}
>>> printSum(listOf(1, 2, 3))
6
複製代碼
上述例子中,c
是否擁有類型List<Int>
的檢查是可行的,由於在編譯器就肯定了集合(無論它是列表仍是其餘類型的集合)包含的是整型數字。code
本小節展現inline
函數的另外一種場景:它們的類型參數能夠被實化,使用到的關鍵字——「reified
」。orm
// 如今代碼能夠編譯
fun <T> isA(value: Any) = value is T
Error: Cannot check for instance of erased type: T
// 如今代碼能夠編譯
inline fun <reified T> isA(value: Any) = value is T
fun isATest() {
println(isA<String>("abc"))
println(isA<String>(123))
}
// 輸出結果
true
false
複製代碼
// 「reified」聲明瞭類型參數不會再運行時被擦除
public inline fun <reified T>
Iterable<*>.filterIsInstance(): List<T> {
val destination = mutableListOf<T>()
for (element in this)
// 能夠檢查元素好似不是指定爲類型實參的類的實例
if (element is T)
destination.add(element)
return destination
}
複製代碼
爲何實化只對內聯函數有效 編譯器把實現內聯函數的字節碼插入每一次調用發生的地方。由於生成的字節碼引用了具體類,而不是類型參數,它不會被運行時發生的類型參數擦除影響。對象
注意:帶
reified
類型參數的inline
函數不能
在Java
代碼中調用。普通的內聯函數能夠像常規函數那樣在Java中調用——它們能夠被調用而不能被內聯。element
具體來講,能夠按下面的方式**使用實化類型參數
**:
is、!is、as、as?
)::class
)java.lang.Class
**(::class.java
)**不能
**作下面這些事情:
reified
**