Generic

James Lin
5 min readSep 9, 2018

--

最近看到 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
}

--

--

James Lin
James Lin

No responses yet