(二十一)Kotlin简单易学 基础语法-泛型

(二十一)Kotlin简单易学 基础语法-泛型

定义泛型类

泛型类的构造函数可以接受任何类型。
MagicBox类指定的泛型参数由放在一对<>里的字母T表示,T是个代表item类型的占位符。MagicBox类接受任何类型的item作为主构造函数值(item:T),并将item值赋值给同样是T类型的subject私有属性。


class MadBox<T>(item: T) {
    private var subject: T = item
}

class Boy(val name:String,val age:Int)

class Dog(val weight:Int)
fun main() {
    val madBox = MadBox(Boy("Jack", 10))
    val madBox2 = MadBox(Dog( 10))

}

泛型参数通常使用字母T(代表英文type)表示,当然,想用其他字母,甚至是英文单词都是可以的。不过,其他支持泛型的语言都在这个约定俗成的T,所以建议你继续用它,这样写出的代码别人更容易理解。

泛型函数

泛型参数也用于函数
定义一个函数用于获取元素,当且仅当MagicBox可用时,才能获取元素。


class MadBox<T>(item: T) {
    var availabel = false
    private var subject: T = item

    fun fetch(): T? {
        return subject.takeIf { availabel }
    }
}

class Boy(val name: String, val age: Int)

class Dog(val weight: Int)

fun main() {
    val madBox = MadBox(Boy("Jack", 10))

    madBox.availabel =true
    madBox.fetch()?.run {
        println("you find $name")
    }

}

多泛型参数

泛型函数或泛型类也可以有多个泛型参数

class MadBox<T>(item: T) {
    var availabel = false
    private var subject: T = item

    fun fetch(): T? {
        return subject.takeIf { availabel }
    }

    // return ->R
    //泛型函数或泛型类也可以有多个泛型参数
    fun <R> fetch(subjectModFunction:(T)->R): R?{
        return subjectModFunction(subject).takeIf { availabel }
    }
}

class Boy(val name: String, val age: Int)
class Man(val name: String, val age: Int)
class Dog(val weight: Int)

fun main() {
    val madBox = MadBox(Boy("Jack", 10))
    val fetch = madBox.fetch {
        Man(it.name, it.age.plus(15))
    }
}

泛型类型约束

如果要确保MagicBox里面只能装指定类型的物品,如Human类型,怎么办?

//泛型类型约束
class MadBox<T :Human>(item: T) {
    var availabel = false
    private var subject: T = item

    fun fetch(): T? {
        return subject.takeIf { availabel }
    }

    // return ->R
    fun <R> fetch(subjectModFunction:(T)->R): R?{
        return subjectModFunction(subject).takeIf { availabel }
    }
}
open class Human(val age: Int)
class Boy(val name: String,  age: Int):Human(age)
class Man(val name: String,  age: Int):Human(age)

class Dog(val weight: Int)

fun main() {
    val madBox = MadBox(Boy("Jack", 10))
    val fetch = madBox.fetch {
        Man(it.name, it.age.plus(15))
    }
}

vararg关键字与get函数

MagicBox能存放任何类型的Human实例,但一次只能放一个,如果需要放入多个实例呢?

//泛型类型约束
class MadBox<T : Human>(vararg item: T) {
    var availabel = false
    private var subject: Array<out T> = item

    fun fetch(index: Int): T? {
        return subject[index].takeIf { availabel }
    }

    // return ->R
    fun <R> fetch(index: Int, subjectModFunction: (T) -> R): R? {
        return subjectModFunction(subject[index]).takeIf { availabel }
    }

    fun a(vararg a: Int) {

    }
}

open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)

class Dog(val weight: Int)

fun main() {
    val madBox = MadBox(
        Boy("Jack", 10),
        Boy("2", 10),
        Boy("3", 10)
    )

    madBox.fetch(2) {
        Man(it.name, it.age)
    }
}

[ ] 函数

//泛型类型约束
class MadBox<T : Human>(vararg item: T) {
    var availabel = false
    private var subject: Array<out T> = item

    fun fetch(index: Int): T? {
        return subject[index].takeIf { availabel }
    }

    // return ->R
    fun <R> fetch(index: Int, subjectModFunction: (T) -> R): R? {
        return subjectModFunction(subject[index]).takeIf { availabel }
    }
    //get函数
    operator fun get(index: Int): T? = subject[index]?.takeIf { availabel }
}

open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)

class Dog(val weight: Int)

fun main() {
    val madBox = MadBox(
        Boy("Jack", 10),
        Boy("2", 10),
        Boy("3", 10)
    )
    madBox[1]
}

out 函数

out(协变),如果泛型类只将泛型类型作为函数(输出),那么使用out,可以称之为生产类/接口,因为它主要生产(produce)指定的泛型。

interface Production<out T> {
    fun product(): T
}

in 函数

in(逆变),如果泛型类只将泛型类型作为函数的入参(输入),那么使用in,可以称为消费者类/接口,因为它主要是用来消费(consume)指定的泛型对象。

interface Consumer<in T> {
    fun consume(item: T)
}

invariant 函数

如果泛型类型即将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么既不用out,也不用in。

interface ProductionConsumer<T> {
    fun product(): T
    fun consume(item: T)
}

何时用out/in

父类泛型对象可以赋值给字类泛型对象,用in
字类泛型对象可以赋值给父类泛型对象,用out

out

interface Production<out T> {
    fun product(): T
}

interface Consumer<in T> {
    fun consume(item: T)
}

interface ProductionConsumer<T> {
    fun product(): T
    fun consume(item: T)
}

open class Food
open class FastFood : Food()
class Burger : FastFood()

//生产者
class FoodStore : Production<Food> {
    override fun product(): Food {
        println("product food")
        return Food()
    }
}

//快餐
class FastFoodStore : Production<FastFood> {
    override fun product(): FastFood {
        println("product fast")
        return FastFood()
    }
}

class BurgerStore : Production<Burger> {
    override fun product(): Burger {
        println("product Burger")
        return Burger()
    }
}

fun main() {
    val foodStore: Production<Food> = FoodStore()
    //字类泛型对象可以赋值给父类泛型对象,用out,在java中是不允许的
    val foodStore2: Production<Food> = FastFoodStore()

}

in


interface Consumer<in T> {
    fun consume(item: T)
}


open class Food
open class FastFood : Food()
class Burger : FastFood()

//消费者
class Everybody : Consumer<Food> {
    override fun consume(item: Food) {
        println("Eat food")
    }
}

class ModernPeople:Consumer<FastFood>{
    override fun consume(item: FastFood) {
        println("Eat fastFood")
    }
}


class American :Consumer<Burger>{
    override fun consume(item: Burger) {
        println("Eat burger")
    }

}



fun main() {
    //父类赋值子类
    val consumer1:Consumer<Burger> = Everybody()
    val consumer2:Consumer<Burger> = ModernPeople()
    val consumer3:Consumer<Burger> = American()

}

reified 函数

有时候,你可能想知道某个泛型参数具体是什么类型,reified关键字能帮你检查泛型参数类型。Kotlin不允许对泛型参数T做类型检查,因为泛型参数类型会被类型擦除,也就是说,T的类型信息在运行时是不可知的,Java也有这样规则。

class MadBox<T : Human>() {

    inline fun <reified T> randomOrBackup(backup: () -> T): T {
        val items = listOf(
            Boy("name", 20),
            Man("name", 20)
        )
        val random = items.shuffled().first()
        return if (random is T) {
            random
        } else {
            backup()
        }
    }
}

open class Human(val age: Int)
class Boy(val name: String, age: Int) : Human(age)
class Man(val name: String, age: Int) : Human(age)
class Dog(val weight: Int)

fun main() {
    val box1: MadBox<Man> = MadBox()
    box1.randomOrBackup {
        Man("name", 20)
    }
}


版权声明:本文为LIU_HONGPENG原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。