iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 17
1

今天我們會提到

  • 泛型定義

  • 限制泛型實際型別

  • in 共變 (contravariance), out 協變(covariant)

如何定義泛型 類別

用泛型來宣告 Box , T 代表泛型型態 , 泛型型態 T 之後可以用其他實際型別代替他


class Box<T>(t: T) {
    var value = t
}

如果要建立 Generics class 的 object 需要給予 type (實際型別)


 val box = Box<Int>(1)

如果可以從 傳入的 參數判斷 type 可以被省略


 val box = Box(1)

限制為特定的 supertype

限制 泛型的實際型別 只能是 特定 supertype 或者是他的 subtype

下面限制 泛型 型別 只能是 Number 或者是他的 subtype


class Box<T:Number>(t: T) {
    var value = t
}

Int 形態和 Double 形態是Number的 subtype,會被接受


Box(1)

Box(0.5)

當你傳入一個 String type 的 value 會報錯

Type mismatch: inferred type is String but Number was expected


Box("1")


in 共變 (contravariance), out 協變(covariant)

當泛型型態 沒有 in 或 out 修飾符時 , 可以說一但有一個型別取代了泛型 T, 這個類別就只能接受這型別

例如 定義下面泛型 類別



class Retailer <T>{


fun sell():T{


}


}

下面變數 類型 泛型型別 Cat , 只能接受 同樣 泛型型別 Cat 的物件


val catRetailer: Retailer <Cat>  = Retailer <Cat>


錯誤,不能接受 泛型型別 Dog 的物件


val catRetailer: Retailer <Cat>  = Retailer <Dog>


out 協變(covariant)

兩種情形可以用 out , 也就是 不能 進入 一個類別

  1. val 變數 型別(只讀不可寫), var 不可

  2. T 泛型型別 是 回傳的型別 , 不可以是傳入參數 的 型別 (參數 是 val 可以)

定義一個變數 Cat Retailer , 泛型型別 是 Cat , 將一個同樣是泛型型別 Cat Retailer class object 傳給他是可以的


val catRetailer: Retailer <Cat>  = Retailer <Cat>


下面是無法編譯的 , 泛型型別 是 Pet 的 Retailer class , 只能接受 同樣 泛型型別 Pet
的 Retailer class 物件 , 並不能接受 泛型型別 為 subtype(cat)的 Retailer class 物件


val petRetailer: Retailer <Pet> = Retailer <Cat>


可以藉由 out 修飾符 , 也就是協變 , 來達成上面



interface Retailer < out T>{


fun sell():T


}

這樣我們就可以把 subtype 泛型型別 的 物件 , 傳遞給 同類別 的 supertype 泛型型別 物件


val petRetailer: Retailer <Pet> = Retailer <Cat>


in 共變 (contravariance)

下面情形可以用 in , 也就是 進入 一個類別 的位置

  1. T 泛型型別 是 傳入的參數的型別

定義 Vet class , treat 傳入的參數的型別 為 T


class Vet<T:Pet>{


fun treat(t:T){



}


}

下面我們定義兩個 Vet 類型的 變數 , 泛型型別 Cat Vet class 物件 , 可以傳遞給 同樣泛型 的類別 , 但 泛型型別 Pet Vet class 物件 則無法傳遞


val Vet1: Vet<Cat> = Vet<Cat>()

val Vet2 : Vet<Cat> = Vet<Pet>()

如果要達成上面可以在 Vet class 泛型 加上 in 修飾符,也就是共變 , 讓泛型 supertype 取代 subtype


class Vet<in T:Pet>{


fun treat(t:T){



}


}

這樣下面這段就可以編譯了


val Vet2 : Vet<Cat> = Vet<Pet>()


區域 in 共變 (contravariance)

如果我們並沒有 讓所有 subtype 的 泛型型別 都接受 subtype 的 泛型型別,我們可
在特定的地方 再加上 in 修飾符

Vet Class 去除 in 修飾符
這樣就限制了 Vet 的 泛型型別 , 讓型別 是不變的 , 只能接受特定型別


class Vet< T:Pet>{


fun treat(t:T){



}


}

下面 泛型型別 限定 Cat,就不能接受其他型別(不管是他的 subtype 或者是 supertype)的物件


val Vet2 : Vet<Cat> = Vet<Pet>()


在 需要的地方 加上 in 修飾符


class Medical<T:Pet> (var vet : Vet<in T>){



}

這樣我們可以將 泛型型別 為 Pet 的 Vet 物件 傳給 泛型型別 為 Cat Medical 類別


val hospital: Medical <Cat> = Medical(Vet<Pet>())


上一篇
[Day 16] Kotlin Lambdas 表達式
下一篇
[Day 18] Kotlin 列舉 Emun class , Sealed class
系列文
Android 菜鳥村-開發基礎 30篇32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言