最近看到 ViewModelFactory 於是就讀到了 Generic。
今天大致介紹一下 我所理解的 Generic
Generic:是可以讓程式更靈活的存在,定義更靈活的 API。
以下只是舉例 只是舉例 只是舉例:
狗與貓都是動物
動物長這樣
open class Animal {
open fun sound() {
println("Hi")
}
}
狗長這樣
class Dog : Animal() {
override fun sound() {
println("Woof Woof")
}
}
貓長這樣
class Cat : Animal() {
override fun sound() {
println("Meow Meow")
}
}
如果定義一個 AnimalList 長這樣
class AnimalList<T : Animal> {
// T:Animal mean T must is Animal
//out T is mean T type just use to output
//in T is mean T type just use to input
var k = ArrayList<T>()
fun add(input: T) {
k.add(input)
}
fun remove(input: T) {
k.remove(input)
}
}
Upper Bound
看到這行
T:Animal
這行就是 Upper Bound ,也就是說 定義 Generic 的 Type 最上層只能到 Animal。
可以這樣定義
var al = AnimalList<Animal>()
var dl = AnimalList<Dog>()
但是不能這樣定義
var al = AnimalList<Any>()
Declaration-site variance
在 Java 裡面為了確保 Type 的安全,這段程式碼是不會通過的。
List<String> sList=new ArrayList<String>();
List<Object> objects=sList;
但是 Kotlin 有一種東西叫做 Declaration-site variance,他支援這種寫法。
為了確保 run time 的安全,在 Generic 必須定義 T 為 out,也就是說 T 這個 Type 只能 output 不能 input。
只能讀取資料的稱為 producer
只能寫入資料的稱為 consumer
PECS ( Producer-Extends, Consumer-Super)
這個 interface 只有一個 getAnimal 的方法,也就是說沒有寫入資料的方法,所以 data 是安全的,絕對沒有型態的問題。
interface AnimalOut<out T> {
fun getAnimal(): T
}
實作 Interface
class AnimalOutImp<T> : AnimalOut<T> {
var target: T
override fun getAnimal(): T {
return target
}
constructor(ani: T) {
target = ani
}
}
fun main(argu: Array<String>) {
var a1: AnimalOut<String> = AnimalOutImp<String>("Hello")
var a2: AnimalOut<Any> = a1
}
這一段程式碼在 Java compile 是不會過的,
因為 Java 認為 data 的集合是 invariant,也就是說 List<String> 不是 List<Object> 的 Child。
kotlin 支援 variant,AnimalOut 並沒有 consumer 的方法(寫入資料),那麼把 Animal<String> 賦予 Animal<Any>,是非常安全的,並沒有型態的問題。
out 用於只能 produce 不能 consume。
kotlin 也支援 contravariant。
in 用於只能 consume 不能 produce。
Kotlin 將此技術用於 compareTo
Create 一個 Interface,把 T 宣告成 in,compareTo 是用於比較,所以不會 produce 任何的 data。
interface AnimalIN<in T> {
operator fun compareTo(other: T): Int
}
Animal 實作 compareTo
open class Animal : AnimalIN<Animal> {
override fun compareTo(other: Animal): Int {
if (other.id > this.id) {
return 1
} else {
return 0
}
}
var id = 0
open fun sound() {
println("Hi")
}
}
由於 Dog 也是 Animal 的一種,加上 AnimalIN 裡面並不會 produce 任何的 data,所以以下程式碼是安全的。
fun main(argu: Array<String>) {
var x: AnimalIN<Animal> = Dog()
var y: AnimalIN<Dog> = x
}