16.Kotlin星投影與泛型約束詳解
星投影
star projection(星投影)
Star<out T>
:如果T
的上界是TUpper
,那麼Star<*>
就相當於Star<out T>
,這表示當T
的型別未知時,你可以Star<*>
當中安全地讀取TUpper
型別的值。
Star<in T>
:Star<*>
就相當於Star<in Nothing>
,這表示你無法向其中寫入任何值。
Star<T>
,如果T
的上界為TUpper
,那麼Star<*>
就相當於讀取時的Star<out TUpper>
以及寫入時的Star<in Nothing>
。
如果泛型型別具有多個型別引數,則每個型別引數都可以單獨投影。 例如,如果型別被宣告為interface Function <in T, out U>
,我們可以想象以下星投影:
Function<*, String>
表示 Function<in Nothing, String>
;Function<Int, *>
表示Function<Int, out Any?>
;Function<*, *>
表示 Function<in Nothing, out Any?>
。
注意:星投影非常像 Java 的原始型別,但是安全。
示例程式碼
class Start<out T> {
}
class Star2<in T> {
fun setValue(t: T) {
}
}
class Star3<T>(private var t: T) {
fun setValue(t: T) {
}
fun getValue(): T {
return this.t
}
}
fun main(args: Array<String>) {
val star: Start<Number> = Start<Int>()
val star2: Start<*> = star
val star3: Star2<Int> = Star2<Number>()
val star4: Star2<*> = star3
//star4.setValue(3) //compile error
val star5: Star3<String> = Star3<String>("hello")
val star6: Star3<*> = star5
star6.getValue()
//star6.setValue("") //compile error
val list: MutableList<*> = mutableListOf("hello", "world", "hello world")
println(list[0])
//list[0] = "test" //compile error
}
泛型擦除
Kotlin 為泛型宣告用法執行的型別安全檢測僅在編譯期進行。 執行時泛型型別的例項不保留關於其型別實參的任何資訊。 其型別資訊稱為被擦除。例如,Foo<Bar> 與 Foo<Baz?> 的例項都會被擦除為 Foo<*>。
示例程式碼
package com.leofight.kotlin2
class MyStorage<out T>(private var t: T) {
fun getValue(): T {
return this.t
}
fun setValue(t: @UnsafeVariance T) {
this.t = t
}
}
fun main(args: Array<String>) {
var myStorage1: MyStorage<Int> = MyStorage(5)
val myStorage2: MyStorage<Any> = myStorage1
println(myStorage2.getValue())
myStorage2.setValue("hello")//泛型擦除
println(myStorage2.getValue())
}
泛型函式
不僅類可以有型別引數。函式也可以有。型別引數要放在函式名稱之前:
fun <T> getValue(item: T): T {
return item
}
要呼叫泛型函式,在呼叫處函式名之後指定型別引數即可:
fun main(args: Array<String>) {
val item = getValue<Int>(3)
println(item)
val item2 = getValue("hello")
println(item2)
}
泛型約束
能夠替換給定型別引數的所有可能型別的集合可以由泛型約束限制。
上界
最常見的約束型別是與 Java 的 extends 關鍵字對應的 上界:
fun <T : Comparable<T>> sort(list: List<T>) {
// ……
}
冒號之後指定的型別是上界:只有 Comparable<T> 的子型別可以替代 T。 例如:
sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子型別
sort(listOf(HashMap<Int, String>())) // 錯誤:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子型別
預設的上界(如果沒有宣告)是 Any?。在尖括號中只能指定一個上界。 如果同一型別引數需要多個上界,我們需要一個單獨的 where-子句:
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
相關文章
- Go 泛型之泛型約束Go泛型
- 泛型的約束理解泛型
- C#泛型約束C#泛型
- Java泛型(三):型別擦除帶來的約束與侷限性Java泛型型別
- C#中泛型約束(where)是什麼?C#泛型
- MySQL——約束(constraint)詳解MySqlAI
- XML Schema 字串資料型別及約束詳解XML字串資料型別
- Java 泛型詳解Java泛型
- 資料型別與約束資料型別
- [譯]Kotlin泛型中何時該用型別形參約束?Kotlin泛型型別
- java 之泛型與可變引數詳解Java泛型
- Java的泛型詳解(一)Java泛型
- 詳解C#泛型(一)C#泛型
- 詳解C#泛型(三)C#泛型
- 詳解C#泛型(二)C#泛型
- Java基礎-泛型詳解Java泛型
- 深度學習中的Lipschitz約束:泛化與生成模型深度學習模型
- Java泛型詳解,史上最全圖文詳解!Java泛型
- 【.NET】利用 IL 魔法實現隨心隨意的泛型約束泛型
- 泛型程式設計詳解(一)泛型程式設計
- 學員優秀博文賞析:泛型萬用字元及約束泛型字元
- Android 約束佈局(ConstraintLayout)1.1.0 版詳解AndroidAI
- TreeSet的null值與元素型別的約束Null型別
- 【SQL】15 SQL 約束(Constraints)、NOT NULL 約束、UNIQUE 約束、PRIMARY KEY 約束、FOREIGN KEY 約束、CHECK 約束、DEFAULT約束SQLAINull
- Sqlserver中所有約束的型別,建立、修改與刪除SQLServer型別
- Java核心知識1:泛型機制詳解Java泛型
- Rust 泛型與特性Rust泛型
- SQL教程——常見的約束型別SQL型別
- 委派與約束委派復現
- Go 泛型變更:約束太醜了,先移動到 x/exp 做實驗性功能Go泛型
- 差分約束基本講解
- 好程式設計師Java培訓之泛型繼承原理與用法詳解程式設計師Java泛型繼承
- 約束
- C#語言入門詳解(劉鐵錳)---泛型C#泛型
- Java泛型理解與使用Java泛型
- Javaweb-約束-外來鍵約束JavaWeb
- Java中泛型的詳細解析,深入分析泛型的使用方式Java泛型
- [20191206]nvl與非空約束.txt
- Django模型之欄位與約束Django模型