Android学习Kotlin之六、泛型-扩展函数

Kotlin泛型-扩展函数

泛型的函数或者变量可以接收任何类型;扩展可以在不直接修改类定义的情况下增加类功能,扩展可以用于自定义类,也可以用于比如List、String, 以及Kotlin标准库里的其他类。和继承相似,扩展也能共享类行为,在你无法接触某个类定义,或者某个类没有使用open修饰符,导致你无法继承它时,扩
展就是增加类功能的最好选择。

Kotlin

其它Kotlin文章
Android学习Kotlin之一、常量-条件-函数-高阶函数
Android学习Kotlin之二、Null安全 -字符串操作- 类型转换
Android学习Kotlin之三、标准库函数-集合List-Set-Map
Android学习Kotlin之四、定义类-初始化-继承
Android学习Kotlin之五、对象-接口-抽象类
Android学习Kotlin之六、泛型-扩展函数

本编文章会讲到的知识点

  • 泛型
    • 定义泛型
    • 注意
    • 泛型函数
    • 多泛型参数
    • 泛型类型约束
    • vararg关键字
    • []操作符获取
    • out(协变) in(逆变)
    • out&in案例
    • reified
  • 扩展函数
    • 定义扩展函数
    • 泛型扩展函数
    • 扩展属性
    • infix关键字
    • 带接收者的函数字面量

泛型

定义泛型

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

/**
 * 定义泛型
 */
class GenericityBox(item:T){
    var able = false
    private var mSub:T = item

    fun getT(): T? {
        return mSub.takeIf { able }
    }

    fun  getTR(subi:(T)->R): R ?{
        return subi(mSub).takeIf { able }
    }
}

class Gen(var name:String,var age:Int)
class Cup(val name:String)

注意

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

泛型函数

泛型参数也可以用于函数,定义一个函数用于获取泛型。

// 1.定义泛型类
class GenericityBox(item:T){
    var able = false
    private var mSub:T = item
    fun getT(): T? {
        return mSub.takeIf { able }
    }
}

    // 2.使用泛型函数
    val box3:GenericityBox = GenericityBox(Gen("小明",12))
    val box4:GenericityBox = GenericityBox(Cup("小芳"))

    box3.getT()?.run {
        println("box3=$name")
    }
    box4.able = true
    box4.getT()?.run {
        println("box4=$name")
    }
图片.png

多泛型参数

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

 // 1. 定义泛型
class GenericityBox(item:T){
    var able = false
    private var mSub:T = item

    fun  getTR(subi:(T)->R): R ?{
        return subi(mSub).takeIf { able }
    }
}

    // 2.使用多泛型参数
    val box5:GenericityBox = GenericityBox(Gen("小明",16))
    box5.able = true
    val cup = box5.getTR {
        Cup(it.name)
    }
    println("cup=${cup?.name}")// 打印cup=小明

泛型类型约束

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

 // 1.泛型类型约束 指定类型为Gen
class GenericityBox2(item:T){
    private var mSub:T = item
    fun getName():String{
        return mSub.name
    }
}
    //2.使用泛型类型约束
    val box6:GenericityBox2 = GenericityBox2(Gen("小鹏",18))
//    val b:GenericityBox2 报错 泛型指定为Gen
    println("box6=${box6.getName()}")//打印小鹏

vararg关键字

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

// 1.定义一个指定类型的类,并且接收多个对象
class GenericityBox3(vararg item : T){
    var able = false
    var mSub: Array = item
    fun getT(index:Int):T{
        return mSub[index]
    }
}
    //2.使用vararg关键字 传入多个Gen对象
    val box7:GenericityBox3 = GenericityBox3(Gen("小张",10),Gen("小六",12))
    println("box7=${box7.getT(1)}")//打印 Gen(name=小六, age=12)

[]操作符获取

想要通过[ ]操作符取值,可以重载运算符函数get函数。

// 1.重载运算符函数get函数
class GenericityBox4(vararg item : T){
    var mSub: Array = item

    operator fun get(index: Int) :T{
        return mSub[index]
    }
}
// 2.使用[]操作符取值
val box8:GenericityBox4 = GenericityBox4(Gen("小张",10),Gen("小六",12))
println("box8=${box8[0]}")

out(协变) in(逆变)

out(协变)

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

// 泛型只作为函数的输出
interface ProductionOut{
    fun onOut():T
}

in(逆变)

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

// 泛型只作为函数的入参
interface ProductionIn{
    fun onIn(item:T)
}

Invariant(不变)

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

// 即是协变又是逆变
interface ProductionOutIn{
    fun onOut():T
    fun onIn(item:T)
}

out&in案例

1.定义生产接口和消费接口,先不使用out和in

interface OnProductionOut{
    fun onOut():T
}
interface OnConsumerIn{
    fun onIn(item:T)
}

2.定义三个类 食物 快餐 汉堡,分别是集成关系

open class Food
open class FastFood:Food()
class Burger:FastFood()
  • 使用out
    1.定义三个生产食物的类分别实现OnProductionOut类
class FoodOut:OnProductionOut{
    override fun onOut(): Food {
        println("FoodOut")
        return Food()
    }
}
class FastFoodOut:OnProductionOut{
    override fun onOut(): FastFood {
        println("FastFoodOut")
        return FastFood()
    }
}
class BurgerOut:OnProductionOut{
    override fun onOut(): Burger {
        println("BurgerOut")
        return Burger()
    }
}

2.赋值使用

java中使用泛型初始化左侧定义的泛型一定要和右侧初始化的泛型一致,比如 List = new List 会报错,kotlin中也是一样

val mProduction1 : OnProductionOut = FoodOut() // 不报错, FoodOut()定义的泛型是Food
val mProduction2 : OnProductionOut = FastFoodOut() // 报错,FastFoodOut()定义的泛型是FastFood,而左侧定义类型是Food 
val mProduction3 : OnProductionOut = BurgerOut()// 报错,BurgerOut()定义的泛型是Burger,而左侧定义类型是Food 

报错

3.修改接口添加out,再使用就不会报错了,因为子类泛型对象可以赋值给父类泛型对象

// 协变
interface OnProductionOut{
    fun onOut():T
}

// 逆变
interface OnConsumerIn{
    fun onIn(item:T)
}

val mProduction1 : OnProductionOut = FoodOut()
val mProduction2 : OnProductionOut = FastFoodOut()
val mProduction3 : OnProductionOut = BurgerOut()
使用out
  • 使用in
    1.定义三个消费者
class FoodIn:OnConsumerIn{
    override fun onIn(item: Food) {
        println("FoodIn")
    }
}

class FastFoodIn :OnConsumerIn {
    override fun onIn(item: FastFood) {
        println("FastFoodIn")
    }
}

class BurgerIn:OnConsumerIn {
    override fun onIn(item: Burger) {
        println("BurgerIn")
    }
}

2.使用

    val mConsumer1 : OnConsumerIn = FoodIn()// 报错,左侧泛型和右侧泛型类型不一致
    val mConsumer2 : OnConsumerIn = FastFoodIn()// 报错,左侧泛型和右侧泛型类型不一致
    val mConsumer3 : OnConsumerIn = BurgerIn()// 不报错 左侧泛型和右侧泛型类型一致
泛型类型报错

3.修改接口添加in,再使用就不会报错了,因为父类泛型对象可以赋值给子类泛型对象

interface OnConsumerIn{
    fun onIn(item:T)
}
val mConsumer1 : OnConsumerIn = FoodIn()
val mConsumer2 : OnConsumerIn = FastFoodIn()
val mConsumer3 : OnConsumerIn = BurgerIn()

reified

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

  • 错误写法

Kotlin不 允许对泛型参数T做类型检查,因为泛型参数类型会被类型擦除


图片.png
  • 使用inline reified


    图片.png
/**
 * 1.定义测试类
 */
class GenericityBox5 {

    /**
     * 需求 从集合中随机一个对象 判断随机的对象是不是穿过来的类型 如果是就返回随机对象,否则就执行传过来的方法
     */
    inline fun  randomOrBackup(backup: () -> T): T {
        val mList: MutableList = mutableListOf(
            ReifiedSon1("小明"),
            ReifiedSon2("小芳")
        )
        val random = mList.shuffled().first()
        println("random=$random")
        return if (random is T) {
            random
        } else {
            backup()
        }
    }

}

//2.使用
    val box9: GenericityBox5 = GenericityBox5()
    val bean = box9.randomOrBackup {
        println("其他操作。。。")
        ReifiedSon1("小明")
    }
    println("bean=$bean")
  • 随机的类型和泛型类型一致


    图片.png
  • 随机的类型和泛型类型不一致


    图片.png

扩展函数

定义扩展函数

定义扩展函数和定义一般函数差不多,但有一点大不一样,除了函数定义,你还
需要指定接受功能扩展的接收者类型,后面会说到。

// 给字符串后面添加内容
fun String.addS(s: String) = this + s

// 给Any类添加一个函数
fun Any.easyPrint() = println(this)

    // 使用扩展函数
    println("小明".addS("HHHHHH"))
    "郭襄".easyPrint()
    6666.easyPrint()

图片.png

泛型扩展函数

新的泛型扩展函数不仅可以支持任何类型的接收者,还保留了接收者的类型信息,使用泛型类型后,扩展函数能够支持更多类型的接收者,适用范围更广了。

// 泛型扩展函数 打印之后再把当前对象返回给调用者
fun  T.easyPrint2(): T {
    println(this)
    return this
}

"杨过".easyPrint2().addS("爱小龙女").easyPrint()
图片.png
  • let扩展函数

泛型扩展函数在Kotlin标准库里随处可见,例如let函数,let函数被定义成了泛型
扩展函数,所以能支持任何类型,它接收一-个lambda表达式,这个lambda表达
式接收者T作为值参,返回的R-lambda表达式返回的任何新类型。

图片.png

扩展属性

除了给类添加功能扩展函数外,你还可以给类定义扩展属性,给String类添加一个扩展,这个扩展属性可以统计字符串里有多少个元音字母。

//定义扩展属性
val String.numV
    get() = count { "abc".contains(it) }

// 使用扩展属性
"asdfgzxc".numV.easyPrint()// 打印2

infix关键字

infix关键字适用于有单个参数的扩展和类函数,可以让你以更简洁的语法调用函数,如果一个函数定义使用了infix关键字,那么调用它时,接收者和函数之间的点操作以及参数的一对括号都可以不要。

// 定义一个可空并使用infix修饰的扩展函数
infix fun String?.setName(d:String) = println(this ?: d)

// 可空变量
    var name:String? = null
// 普通使用
    name.setName("小笼包")
// 省略使用
    name setName "贾玲"
infix使用

带接收者的函数字面量

apply函数是如何做到支持接收者对象的隐式调用的。

图片.png

你可能感兴趣的:(Android学习Kotlin之六、泛型-扩展函数)