首页移动开发Kotlin 泛型:基本使用

Kotlin 泛型:基本使用

时间2023-03-27 16:11:25发布访客分类移动开发浏览806
导读:泛型在 Kotin 的日常使用中运用很广泛:当我们使用 List、Array 等类型时,我们会使用到泛型类;当我们使用 apply、let 等函数时,我们会使用到泛型函数。在 Kotlin 中声明和使用泛型类、泛型函数的基本概念和 Java...

泛型在 Kotin 的日常使用中运用很广泛:

  1. 当我们使用 List、Array 等类型时,我们会使用到泛型类;
  2. 当我们使用 apply、let 等函数时,我们会使用到泛型函数。

在 Kotlin 中声明和使用泛型类、泛型函数的基本概念和 Java 相似,有 Java 泛型概念的情况下,不用详细解释或者做进一步了解,也能够很容易地上手使用泛型。

但使用泛型仅仅是第一步,要想将泛型应用好,仍然需要做进一步深入的学习。

本篇是 Kotlin 泛型的基础介绍,进阶内容可点击链接查看。

  • Kotlin 泛型:基本使用
  • Kotlin 泛型:类型参数约束

系列持续更新中,欢迎关注订阅。

为什么需要泛型

假如我们想实现自定义的列表类型,用于存放数值、字符串或其他具体的类型。我们可以这么写:

// 数值列表
interface NumberList {

    fun set(index: Int, obj: Number?)
    fun get(index: Int): Number?
}


// 字符串列表
interface StringList {

    fun set(index: Int, obj: String?)
    fun get(index: Int): String?
}


// Car 列表
class Car 
interface CarList {

    fun set(index: Int, obj: Car?)
    fun get(index: Int): Car?
}

如果没有泛型,我们只能针对每种具体的类型,分别定义对应的列表,这种方式只能针对有限的具体类型进行实现、不同具体类型的列表实际上具有相似的实现,这些代码只能在不同列表间拷贝重复,无法复用,难以维护。

有的同学会用这样的方法来解决上面的问题:

interface AnyList {

    fun set(index: Int, obj: Any?)
    fun get(index: Int): Any?
}
    

这个方法虽然能解决上述问题,但它带来了其他的问题。

首先,列表中存放的数据类型信息消失了,从函数签名上,我们只知道能得到一个实例,但这个实例具体是什么类型就无从得知,作为列表的使用者,面对一个未知的接口,开发体验别提有多糟糕了。

其次,Kotlin 是静态类型语言,静态类型语言的优势是能够在编译时帮我们提前进行类型检查,保证类型的正确性,避免潜在的类型错误。而上面这个例子,由于任何类型都是 Any 类型的子类,在进行类型检查时,Kotlin 无法帮我们检查出不合理的调用,我们完全可以往一个 String 列表里放入一个 Number 实例,从而让使用者从一个 Car 列表中得到猫猫狗狗,这都是完全有可能的。这种看似灵活的万能列表,实际上是随时会爆炸的炸弹,严重降低了工程质量(请注意倒车,请注意倒车。

什么是泛型

泛型提供了一种方法,允许我们定义带「类型参数」的泛型类/泛型函数,在创建泛型类的实例、调用泛型函数时,「类型参数」将替换成具体的「类型实参」。

上面的例子用泛型定义将会很方便简洁,同时,类型信息得到了保留,编译器也能正常进行类型检查:

interface ListT>
 {

    fun set(index: Int, obj: T?)
    fun get(index: Int): T?
}
    

val stringList: ListString>
     = // ... 省略
stringList.set(0, "a string") // OK
stringList.get(0)?.charAt(0) // OK
stringList.set(0, 1) // 编译出错,类型不匹配
stringList.get(0) - 1 // 编译出错,类型不匹配

class Car
val carList: ListCar>
     = // ... 省略
carList.set(0, Car()) // OK
carList.get(0) is Car? // Always true
carList.set(0, 1) // 编译出错,类型不匹配
carList.get(0) is Int? // 编译出错,类型不匹配

泛型机制允许我们在编码的时候,使用占位符作为类型(即「类型参数」代替实际使用时的类型(即「类型实参」)。

如何区别上述两个概念?

当我们在「定义」泛型类、泛型函数时,我们使用的是「类型参数」;

当我们在「使用」泛型类、泛型函数时,我们使用的是「类型实参」。

「类型参数」是占位符,就像变量一样,可以任意取名,一般使用单个大写字母(T、U、V)、全大写单词(DATA、TOKEN)、或首字母大写的单词(Data、Token);

「类型实参」是具体的类型,只能传入已存在的具体类型,如 IntStringAny 或者其他自定义的具体类型。

定义泛型类、泛型函数的方式如下:

// --- 泛型函数 ---
fun P>
     run(param: P) // 仅用于函数参数,定义在泛型类、泛型接口中
fun R>
     run(): R // 仅用于函数返回值,定义在泛型类、泛型接口中
fun P, R>
     invoke1(param: P): R // 用于函数参数和返回值,定义在泛型类、泛型接口中
fun T>
     filter(predicate: (T) ->
     Boolean) // 用于高阶函数

// --- 泛型类 ---
class BoxT>
 {
 // 泛型类
    private var instance: T? // 用于属性
    // 类中的泛型函数
    fun get(): T? {
 // 用于方法,下同
        return instance
    }

    fun set(instance: T?) {

        this.instance = instance
    }

}
    
interface ListT>
 {
 // 泛型接口
    fun set(index: Int, obj: T) // 用于方法,下同
    fun get(index: Int): T?
}
    

使用泛型类、泛型函数:

// 使用泛型函数
filterString>
 {
     it: String ->
 false }
    

// 使用泛型类
val stringBox = BoxString>
    ()

// 使用泛型接口
class Car
class CarList : ListCar>
 {

    // 实现接口中的泛型函数
    override fun set(index: Int, obj: Car) {

        // todo
    }

    override fun get(intdex: Int): Car? {

        // todo
    }

}
    
val carList = CarList()
carList.set(0, Car())
carList.get(0) is Car? // Always true

了解到这里,就掌握了基本的泛型使用方式:

  1. 用「类型参数」作为占位符,定义泛型类、泛型函数
  2. 使用泛型类、泛型函数时,需要传递具体类型作为「类型实参」。

下一篇文章,将介绍 Kotlin 泛型的进阶知识:类型参数约束

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

androidkotlin面向对象编程移动开发

若转载请注明出处: Kotlin 泛型:基本使用
本文地址: https://pptw.com/jishu/389.html
一段因 @State 注入机制所产生的“灵异代码” 国内移动应用开发平台哪家强?(国内移动应用开发平台哪家强一些)

游客 回复需填写必要信息