Generic

James Lin
James Lin
Sep 9, 2018 · 5 min read

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

Written by

James Lin

live for passion

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade