• kotlin 泛型


    一、泛型使用

            泛型,即 "参数化类型",将类型参数化,可以用在类,接口,函数上。

            与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。

    1.1 泛型的优点
    类型安全:通用允许仅保留单一类型的对象。泛型不允许存储其他对象。

    不需要类型转换:不需要对对象进行类型转换。

    编译时间检查:在编译时检查泛型代码,以便在运行时避免任何问题。

    1.2 泛型类
            TFood类指定的泛型参数由放在一对<>里的字母T表示,T是个代表item类型的占位符。TFood类接受任何类型的item作为主构造函数值(item: T)。

    1. //1、创建泛型类
    2. class TFood<T>(item:T) {
    3. init {
    4. println(if(item is TApple){(item as? TApple)?.price}else item)
    5. }
    6. }
    7. //2、创建传入的类
    8. class TApple(var price :Int)
    9. //3、使用
    10. fun main() {
    11. //传入Int类型
    12. TFood(30)//30
    13. //传入String类型
    14. TFood("水果")//水果
    15. //传入实体对象
    16. TFood(TApple(13))//13
    17. }

    1.3 泛型函数

    泛型参数也可以用于函数。

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

    1.4 泛型接口


     

    1. interface IFoodEffect<T>{
    2.     fun effect(item:T)
    3. }
    4. //实现接口
    5. class Banana:IFoodEffect<String>{
    6.     override fun effect(item: String) {
    7.         println(item)//item
    8.     }
    9.  
    10. }
    11.     //使用
    12.     Banana().effect("常食香蕉有益于大脑,预防神经疲劳,还有润肺止咳、防止便秘")

    二、泛型类型约束
    指定参数类型:我想让这个泛型类只能传入某种类型。

    1. open class Vip(price:Int)
    2.  
    3. class TApple(var price: Int): Vip(price)
    4. class TFood<T:Vip>(item: T) {
    5.     ...
    6. }


      T:Vip,这样写就表示这里只能传入Vip和其子类。这个就类似Java的 上界通配符
     

    三、形变

    • out(协变):它只能出现在函数的输出位置,只能作为返回类型,即生产者

    • in(逆变):它只能出现在函数的输入位置,作为参数,只能作为消费类型,即消费者。

    • 默认(不变):如果泛型类既将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么既不用out也不用in。

    3.1 不变

            泛型类型即作为输出又作为参数。

    1. /**
    2. * 泛型接口 不变
    3. */
    4. interface IUnchanged<T> {
    5. fun originally():T
    6. fun originally(item:T)
    7. }
    8. class BigStore:IUnchanged<String>{
    9. override fun originally(): String {
    10. return "实现泛型接口 不变---返回类型"
    11. }
    12. override fun originally(item: String) {
    13. println("------------不变---传参 ---=="+item)
    14. }
    15. }
    16. fun main() {
    17. println("---------------实现泛型接口------- 不变 默认---------------")
    18. var iUnchanged=BigStore()
    19. println(iUnchanged.originally())
    20. println(iUnchanged.originally("参数"))
    21. }

    3.2 out-协变

            它只能出现在函数的输出位置,只能作为返回类型,即生产者

            作用:可以将子类泛型对象可以赋值给父类泛型对象

    1. //out
    2. interface IReturn<out T>{
    3. fun effect():T
    4. }
    5. open class Fruit()
    6. class AppleHn():Fruit()
    7. //生产者
    8. class FruitMarket:IReturn<Fruit>{
    9. override fun effect(): Fruit {
    10. println("FruitMarket effect")
    11. return Fruit()
    12. }
    13. }
    14. class AppleHnMarket:IReturn<AppleHn>{
    15. override fun effect(): AppleHn {
    16. println("AppleHnMarket effect")
    17. return AppleHn()
    18. }
    19. }
    20. fun main() {
    21. //out:可以将子类泛型对象(AppleHn)可以赋值给父类泛型对象(Fruit)
    22. var fm:IProduction = FruitMarket()
    23. println(fm.effect())
    24. //am的引用类型是Fruit对象
    25. //但是AppleHnMarket返回的是AppleHn对象。
    26. //这里将AppleHn对象赋值给Fruit对象并返回。
    27. var am:IProduction = AppleHnMarket()
    28. println(am.effect())
    29. }

    3.3 in-逆变

            它只能出现在函数的输入位置,只能作为参数,即消费者

            作用:可以将父类泛型对象可以赋值给子类泛型对象

    1. //in
    2. interface IConsumer<in T>{
    3. fun spend(t:T)
    4. }
    5. class Animal:IConsumer<Fruit>{
    6. override fun spend(t: Fruit) {
    7. println("Animal spend Fruit")
    8. }
    9. }
    10. class People:IConsumer<AppleHn>{
    11. override fun spend(t: AppleHn) {
    12. println("People spend AppleHn")
    13. }
    14. }
    15. fun main() {
    16. //in:可以将父类泛型对象(Fruit)可以赋值给子类泛型对象(AppleHn)
    17. var fca: IConsumer = Animal()
    18. fca.spend(AppleHn())
    19. println(fca)
    20. var fcp: IConsumer = People()
    21. fcp.spend(AppleHn())
    22. println(fcp)
    23. }

    五、vararg
            泛型类一次只能放一个,如果需要放入多个实例呢?

    1. class BookMany<T : AndroidMany>(vararg item: T) {
    2.     var data: Array<out T> = item
    3. }
    4.  
    5. open class AndroidMany(name: String)
    6. class KotlinMany(var name: String, var price: Int) : AndroidMany(name) {
    7.     override fun toString(): String {
    8.         return "KotlinS(name='$name', price=$price)"
    9.     }
    10. }
    11.  
    12. class JavaMany(var name: String, var price: Int) : AndroidMany(name) {
    13.     override fun toString(): String {
    14.         return "JavaS(name='$name', price=$price)"
    15.     }
    16. }
    17.  
    18. fun main() {
    19.     var book = BookMany(
    20.         KotlinMany("初学者", 18),
    21.         KotlinMany("进阶者", 28),
    22.         KotlinMany("终结者", 38),
    23.         JavaMany("全面者", 35),
    24.     )
    25.     println(book)//com.scc.kotlin.primary.classkotlin.BookMany@3d24753a
    26. }
    27.  


            添加完多个对象,如果直接打印book,那么获取到的是个BookMany对象,那么怎么获取book里面的的对象?

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

    1. class BookMany<T : AndroidMany>(vararg item: T) {
    2.     var data: Array<out T> = item
    3.     operator fun get(index:Int) = data[index]
    4. }
    5.  
    6. fun main() {
    7.     var book = BookMany(
    8.         KotlinMany("初学者", 18),
    9.         KotlinMany("进阶者", 28),
    10.         KotlinMany("终结者", 38),
    11.         JavaMany("全面者", 35),
    12.     )
    13.     println(book)//com.scc.kotlin.primary.classkotlin.BookMany@3d24753a
    14.     println(book[0])//KotlinS(name='初学者', price=18)
    15.     println(book[2])//KotlinS(name='终结者', price=38)
    16. }

  • 相关阅读:
    Oracle考证对我们有什么帮助?
    unity面试八股文 - 框架设计与资源管理
    学习Source Generators之IncrementalValueProvider
    Docker OCI runtime create failed
    Spring拦截器的简单应用
    强化学习------DQN算法
    std::any和枚举类转换
    展讯模块读写IMEI
    Ubuntu18.04开机自动启动终端并运行脚本
    nginx部署vue前端项目,访问报错500 Internal Server Error
  • 原文地址:https://blog.csdn.net/luoj_616/article/details/126918758