1、泛型的實化
Java中泛型是在JDK1.5引入的,是一個偽泛型,它是通過泛型擦除機制來實現(xiàn)的。泛型只存在編譯時期,運行時泛型就會被擦除,所以我們無法在運行時獲取泛型的類型信息。
Kotlin最終也會編譯生成和Java相同規(guī)格的class文件,所以Kotlin中的泛型也會被擦除,所以我們無法使用a is T 和 T::class.java。但是Kotlin提供了一個內(nèi)聯(lián)函數(shù),在編譯時就會將內(nèi)聯(lián)函數(shù)的代碼替換到實際調(diào)用的地方,所以對于內(nèi)聯(lián)函數(shù)來說是不存在泛型的擦除的。
所以我們是可以將內(nèi)聯(lián)函數(shù)中的泛型進行實化的,泛型實化的前提有2個:
- 1、必須泛型函數(shù)且是內(nèi)聯(lián)函數(shù)
- 2、聲明泛型的地方加上關(guān)鍵字
reified修飾(reified只能用于內(nèi)聯(lián)泛型函數(shù))
具體代碼如下:
inline fun <reified T> getType() {}
通過泛型實化可以獲取泛型類型的功能,創(chuàng)建Reified.kt,并在其中定義一個頂層函數(shù)
inline fun <reified T> getType() = T::class.java
調(diào)用getType()
fun kotlinTest() {
val stringType:Class<String> = getType<String>()
val intType:Class<Int> = getType<Int>()
println("stringType:${stringType.name} , intType:${intType.name}")
}
打印結(jié)果如下:
stringType:java.lang.String , intType:java.lang.Integer
可以看到我們在運行時獲取到了泛型的類型。
泛型實化的使用場景
先看下打開Activity的代碼
fun openActivity(){
val intent=Intent(this,MainActivity::class.java)
startActivity(intent)
}
下面看下如何通過泛型的實化來實現(xiàn)這個功能
inline fun <reified T> openActivity(context: Context){
val intent=Intent(context,T::class.java)
context.startActivity(intent)
}
但是問題又來了,怎么使用Intent進行數(shù)據(jù)的傳輸呢?其實很簡單,我們可以使用高階函數(shù)來實現(xiàn),具體傳輸?shù)臄?shù)據(jù)由調(diào)用者來完成。
inline fun <reified T> openActivity(context: Context, block: Intent.() -> Unit) {
val intent = Intent(context, T::class.java)
intent.block()
context.startActivity(intent)
}
調(diào)用就很簡單了
openActivity<MainActivity>(this) {
putExtra("name", "LiLei")
putExtra("age", 12)
}
這樣我們就完成了打開頁面通用的方法,可以把openActivity()函數(shù)定義成頂層函數(shù),這樣我們就可以在任意位置調(diào)用了,不過泛型的實化是無法在Java中調(diào)用的。下面看下在Java中如何調(diào)用openActivity()
ReifiedKt.<MainActivity>openActivity(context, new Function1<Intent, Unit>() {
@Override
public Unit invoke(Intent intent) {
intent.putExtra("name", "LiLei");
intent.putExtra("age", 12);
return null;
}
});
報錯信息如下:
openActivity(android.content.Context, kotlin.jvm.functions.Function1<? super android.content.Intent,kotlin.Unit>)' has private access in 'com.example.abu.serviceproject.ReifiedKt
可以看到在Java中無權(quán)訪問該方法的。
2、泛型的協(xié)變
一個泛型類或泛型接口中的方法,方法的參數(shù)列表是接收數(shù)據(jù)的,稱為in位置,方法的返回值是返回數(shù)據(jù)的,稱為out位置。
在講解泛型的協(xié)變之前,先看個例子:創(chuàng)建一個Person類、Student類、Teacher類,讓Student和Teacher繼承Person類。
open class Person(var name: String, var age: Int) {}
class Student(name: String, age: Int) : Person(name, age)
class Teacher(name: String, age: Int) : Person(name, age)
創(chuàng)建一個方法,接收的參數(shù)是Person對象
fun handleData(person: Person){}
該方法接收Person對象,那么能不能向其中傳入Student對象或Teacher對象?
Student和Teacher都是Person的子類,所以是肯定可以的。下面我們修改下handleData()接收的參數(shù)為List<Person>對象
fun handleData(person: List<Person>) {}
那么handleData()能接收List<Student>/List<Teacher>嗎?
在Java中是不允許的,List<Student>/List<Teacher>并不是 List<Person>的子類,否則存在類型轉(zhuǎn)換異常的隱患的。下面通過例子來解析下原因:
創(chuàng)建泛型類SimpleData
class SimpleData<T>() {
private var t: T? = null
fun set(t: T) {
this.t = t
}
fun get(): T? = t
}
創(chuàng)建handleData()函數(shù)接收SimpleData<Person>參數(shù)
fun test() {
val stu = Student("張", 12)
val stuData = SimpleData<Student>()
stuData.set(stu)
handleSimpleData(stuData) //編譯失敗
val result: Student? = stuData.get()
}
fun handleSimpleData(data: SimpleData<Person>) {
val teacher = Teacher("李", 40)
data.set(teacher)
}
由于SimpleData<Student>并不是SimpleData<Person>子類,所以handleSimpleData(stuData)肯定是編譯失敗的,這里假設編譯通過,那么在handleSimpleData()中創(chuàng)建Teacher對象并通過data.set(teacher)替換Student對象,然后通過stuData.get()獲取的是Student對象,實際上返回的是Teacher對象,這就造成了類型轉(zhuǎn)換異常。所以這種寫法是不合法的。
如果不允許修改SimpleData中的數(shù)據(jù),是不是就能解決類型轉(zhuǎn)換異常的問題了。
Kotlin中提供了out關(guān)鍵字來保證泛型只能定義在返回值的位置,不能定義在接收參數(shù)的位置,使用out修改SimpleData類,代碼如下:
class SimpleData<out T>(val t: T) {
fun get(): T = t
}
不是說T不能出現(xiàn)在接收參數(shù)的位置嗎?為什么能出現(xiàn)在構(gòu)造函數(shù)中,這是因為val修飾的t是不允許修改,這也是符合安全規(guī)范的。同理,我們也可以使用private var t:T來代替val t:T。
此時handleSimpleData(data: SimpleData<Person>)中就能接收SimpleData<Student>了
fun test() {
val stu = Student("張", 12)
val stuData = SimpleData<Student>(stu)
handleSimpleData(stuData) //編譯成功
val student: Student = stuData.get()
Log.e(tag,"name is ${student.name} , age is ${student.age}。")
}
fun handleSimpleData(data: SimpleData<Person>) {
val person = data.get()
person.name = "李"
person.age = 40
}
handleSimpleData(data: SimpleData<Person>)中就能接收SimpleData<Student>說明SimpleData<Student>就是SimpleData<Person>的子類,而Student又是Person的子類,這就是Kotlin中泛型的協(xié)變。
3、泛型的逆變
定義一個 MyClass<T> 的泛型類,其中 A 是 B 的子類型,同時 MyClass<B> 又是 MyClass<A> 的子類型,就稱 MyClass 在 T 這個泛型上逆變的。
下面舉個例子來看下:
interface Transform<T> {
fun transform(t: T): String
}
fun main() {
val tranform = object : Transform<Person> {
override fun transform(t: Person): String {
return "name is ${t.name}, age is ${t.age}"
}
}
transformData(tranform)//編譯失敗
}
fun transformData(transform: Transform<Student>) {
val student = Student("張", 12)
transform.transform(student)
}
上面代碼是無法正常的編譯的,我們可以使用逆變來解決上面問題。修改Transform接口
interface Transform<in T> {
fun transform(t: T): String
}
只要加上關(guān)鍵字in就可以實現(xiàn)泛型的逆變了,Transform<Person>也就變成了Transform<Student>的子類了。