泛型約束
fun <T : Comparable<T>> maxOf(a: T, b: T): T {
if (a > b) {
return a
}
return b
}
fun <T> callMax(a: T, b: T) where T : Comparable<T>, T : () -> Unit {
a()
b()
}
泛型的型變
泛型型變分爲三種
-
不變
沒有繼承關係,傳什麼就是什麼,只有一個類型
-
協變: out
- 只讀不寫
- 父類出現的地方可以用子類代替
只讀不寫:
val a: Array<out Number> = Array<Int>(2, { 3 }) a[0] //可讀 a[0] = 2 //ERROR
open class W<out K> { //Error 不可寫 fun add(k: K) { } //可讀 fun get(): K? { return null } }
父類出現的地方,可以使用代替。類似<? extends T>
fun main() { val b = Box<Number>() b.a(Array<Int>(2, { 5 }))//error } class Box<E> { fun a(value: Array<E>) { } }
因爲 E 是 Number,所以傳入 Int 肯定是錯誤的。修改如下:
fun a(value: Array<out E>) {}
out 代表協變,表示繼承自 E 的都可以。這個時候傳入 Int 就沒問題了。因爲 Int 是繼承 Number 的。
注意:
class Box<E> { fun a(value: Array<out E>) { value[0] =1 //Error } }
-
逆變:in
-
只寫不讀。某些情況可以讀,但是隻能往外取放在 Object 中
-
子類出現的地方,可以使用父類代替
只寫不讀
val a: Array<in Int> = Array<Number>(2, { 3 }) val s = a[0] // 不能確定返回值的類型,只能使用Any類型接收 a[0] = 2
open class W<in K> { //可寫 fun add(k: K) { } //返回值 K 報錯,不可讀 fun get(): K? { return null } }
子類出現的地方,可以使用父類代替
fun main() { val b = Box<Int>() b.b(Array<Number>(2, { 5 }))//error } class Box<E> { fun b(value: Array<E>) { } }
E 是 Int,傳入的是 Number。所以報錯,修改如下
fun b(value: Array<in E>) {}
in 代表逆變,表示只要是 E 的父類都可以。所以傳入 Number 就沒問題了。Number 是 int 的父類
注意:
fun b(value: Array<in E>) { val s = value[0] //不能確定返回值的類型,只能使用Any類型接收 }
-
UnsafeVariance
違法形變約束
- 即聲明爲協變的類出現逆變,或者相反
- 聲明爲不變的類接收協變或者逆變類型的參數
class Dustbin<in T> {
//報錯,mutableListOf 本身是協變,但是傳入的是逆變,所以報錯。
val list = mutableListOf<T>()
fun put(t: T) {
list += t
}
}
修改如下:
val list = mutableListOf<@kotlin.UnsafeVariance T>()//使用註解,忽略形變即可
星投影
-
‘*’ 可以用在變量類型的聲明位置
-
‘*’ 可以描述一個未知的類型
-
‘*’ 所替換的類型在:
- 協變點返回泛型參數上限類型
- 逆變點接受泛型參數下限類型
-
不能直接或間接使用在屬性或者函數上
-
對於 Foo ,其中 T 是一個具有上界 TUpper 的協變類型參數,Foo <> 等價於 Foo 。 這意味着當 T 未知時,你可以安全地從 Foo <> 讀取 TUpper 的值。
-
對於 Foo ,其中 T 是一個逆變類型參數,Foo <> 等價於 Foo 。 這意味着當 T 未知時,沒有什麼可以以安全的方式寫入 Foo 。
-
對於 Foo ,其中 T 是一個具有上界 TUpper 的不型變類型參數,Foo<*> 對於讀取值時等價於 Foo 而對於寫值時等價於 Foo。
out:當接收可協變的泛型參數(out T )時, * 映射的類型爲 Any?
open class W<out K> {
//Error
/* fun add(k: K) {
}*/
fun get(): K? {
return null
}
}
fun main() {
val w = W<Number>()
val w1: W<*> = W<Number>()
val s = w1.get() //返回類型爲 Any?
}
下限:當接收可逆變泛型參數(in T) 時,* 映射類型爲 Nothing。
注意:因爲下限無法定義,所以所有的下限類型都爲 Nothing
fun main() {
val w1: W<*> = W<Number>()
w1.add("Nothing")//報錯,只支持 Nothing
}
open class W<in K> {
fun add(k: K) {
}
}
泛型實現原理
僞泛型:如果使用了泛型,則會在編譯時擦除類型,運行時無實際類型生成。這點和 Java一樣
真泛型:C#
泛型類型無法當做真實類型
fun <T> genericMethod(t: T) {
val t = T() //Error
val ts = Array<T>(3){ TODO() } //Error ,Array 在編譯的時候類型時確定的,不會擦除
val cls = T::class.java //Error 無法獲取 class
val list = ArrayList<T>() //可以
}
內聯特化
如果一個函數是內聯函數,那麼他會在調用處執行。一旦在調用的地方執行內聯函數體,我們就可以知道他的類型時確定的了。
聲明如下:
inline fun <reified T> genericMethod(t: T) {
val t = T() //Error
val ts = Array<T>(3){ TODO() }
val cls = T::class.java
val list = ArrayList<T>()
}
例子:
fun <T> fromJson(json: String, classOft: Class<T>): T {
}
fun <T> test(json: String) {
fromJson(json, T::class.java)//Error
}
在調用 fromJson 的時候,無法傳入 T 的class。因爲 T 在編譯的時候會被擦除。
這個時候就可以曲線救國:使用 內聯特化:
inline fun <reified T> test(json: String) {
fromJson(json, T::class.java)//Success
}
這個函數中的代碼會被內聯到調用處,所以泛型的內心自然也就曉得了。
- 泛型原理
- 泛型實現
- 僞類型擦除:在編譯時會擦除爲上限類型
- 真:類型生成
- 內聯特化
- 擴充僞泛型的限制
- 泛型實現
案例:
一般情況下,在繼承關係中,子類可以調用父類的方法,但是父類無法調用子類方法。但是通過接口和泛型可以實現調用子類方法:
typealias OnConfirm = () -> Unit
typealias OnCancel = () -> Unit
private val EmptyFunction = {}
open class Notification(
val title: String,
val content: String
)
class ConfirmNotification(
title: String,
content: String,
val onConfirm: OnConfirm,
val conCancel: OnCancel
) : Notification(title, content)
/**
* 實現 SelfType 接口。泛型是 NotificationBuild 的子類
* 注意 SelfType 有一個屬性 self。是泛型的類型
* 所以 可以通過返回的 self 調用子類的方法。
*/
open class NotificationBuild<Self : NotificationBuild<Self>> : SelfType<Self> {
protected var title = ""
protected var content = ""
fun title(title: String): Self {
this.title = title
return self
}
fun content(content: String): Self {
this.content = content
return self
}
open fun build() = Notification(title, content)
}
//將子類類型作爲泛型傳給父類
class ConfirmNotificationBuilder : NotificationBuild<ConfirmNotificationBuilder>() {
protected var onConfirm: OnConfirm = EmptyFunction
protected var onCancel: OnCancel = EmptyFunction
fun onConfirm(onConfirm: OnConfirm): ConfirmNotificationBuilder {
this.onConfirm = onConfirm
return this
}
fun onCancel(onCancel: OnCancel): ConfirmNotificationBuilder {
this.onCancel = onCancel
return this
}
override fun build(): ConfirmNotification {
return ConfirmNotification(title, content, onConfirm, onConfirm)
}
}
interface SelfType<Self> {
//將 this 強轉爲 泛型的類型
val self: Self
get() = this as Self
}
fun main() {
//創建 ConfirmNotificationBuilder 對象時,就將當前類作爲泛型傳給父類了
ConfirmNotificationBuilder()
.title("標題")
.content("內容")
.onCancel {
println("OnCancel")
}
.onConfirm {
println("OnConfirm")
}
.build()
.onConfirm()
}
-
泛型
-
基本概念:1,泛型參數,2,函數,類添加泛型
-
泛型約束:
- 泛型上限:通過 where 語句添加多個上限
-
泛型形變
- 不變:指定的泛型類型
- 協變:只讀不寫,意思是隻能讀取,不能寫入
- 逆變:只寫不讀,某些情況可以讀,但是隻能是 Any 類型
-
泛型形變點
- 協變點:函數返回值類型爲泛型參數類型
- 逆變點:函數參數爲泛型參數類型
-
UnsafeVariance
- 型變點與泛型不一致是使用這個註解
- UnsafeVariance
-
星投影
- 協變向上,獲取上限
- 逆變向下,獲取下限,所以類型的下限爲 Nothing,不可被添加。
-
泛型原理和內聯特化
- 泛型的擦除,在編譯時,泛型的類型會被擦除。
- 調用某一個泛型方法,無法直接使用泛型,這時就可以使用內聯特化。該方法會在調用處執行。泛型是明確的。
-
模擬Self Type
- 通過泛型參數拿到子類的類型,從而可以調用子類的方法
-