Kotlin 进阶 泛型知识详解

2023-07-13 16:04:36 浏览数 (1)

前言

详细介绍Kotlin中关于泛型的知识。方便我们理解泛型在Kotlin中的使用。

如果对于泛型知识不太了解,也可以通过本篇文章了解泛型到底是什么,并且对我们开发到底有什么作用。

介绍

什么是泛型?我们如果定义类和接口等,针对传入的数据不确定类型的。统一叫做泛型,通过泛型来实现数据类型的动态化。

使用泛型可以最大限度的重用代码,并且保护数据类型的安全以及提高性能。

我们可以在函数声明,属性声明,类声明和接口声明中使用泛型。下面也从这四个维度进行介绍泛型在Kotlin中的使用。

1.声明泛型函数

例如,我们声明一个比较函数。顺便复习一下Kotlin中的伴生对象:Kotlin 进阶 object关键字介绍与学习 (zinyan.com)知识。

代码语言:javascript复制

class Demo {

    companion object zinyan {
        fun <T> isEquals(a: T, b: T): Boolean {
            return a == b
        }
    }
}

fun main(args: Array<String>) {
    val s = Demo.zinyan.isEquals(12L, 231L);
    println(s)
    val s1 = Demo.zinyan.isEquals(13f, 12f);
    println(s1)
    val s2 = Demo.zinyan.isEquals(2, 2);
    println(s2)
}
//输出
false
false
true

其中的companios object 是伴生对象的声明方式,不影响我们使用泛型。

Kotlin中的泛型定义和java中其实很类似。都是使用<T>来进行标注。

小知识:泛型并不是必须用T字母来代替。我们可以使用任意的大写或小写的字母,只是一般情况下大家都是使用T,E,K,U等大写字母而已。 java中的泛型也是可以使用其他字母来代替的。

1.1 多类型定义

我们在上面的示例中只是定义了一种泛型,那么如果有两种甚至更多的数据定义为泛型呢?下面就来介绍多种泛型类型的定义。

示例:

代码语言:javascript复制
fun <T, Z, I, N> isEquals(a: T, b: T, c: Z, d: I, e: N): Boolean {
            return a == b
}

我们只需要在类型后面通过逗号进行分割就可以了。

1.2 泛型约束

我们在上面定义了各种T,Z,I,N,等泛型。但是泛型定义后,到底怎么使用呢?不能乱写吧?这个就是泛型约束了。

两种约束方法:

代码语言:javascript复制
class Demo {

    companion object zinyan {
        fun <T, Z> isEquals(a: T, b: Z): Boolean {
            return a == b
        }
    }
}

fun main(args: Array<String>) {
	//我们在调用函数时约定数据类型
    val s = Demo.zinyan.isEquals<String, String>("12", "213");
   //约定为String 但是传值为int 就会报错了
    var s1 = Demo.zinyan.isEquals<String, String>(12, 23);
}

上面这种是我们接口约束的方法,还有一种情况就是在定义函数的时候,约定传值只能是指定的类以及其父类才行。

示例:

代码语言:javascript复制
class Demo {

    companion object zinyan {
        fun <T : Demo, Z> isEquals(a: T, b: Z): Boolean {
            return a == b
        }
    }
}

例如上面这个示例,定义的泛型T 必须是Demo类和它的子类才行。我们默认没有定义泛型类型其实就是省略了:Any类型而已。

2. 声明泛型属性

我们在上面的示例中了解了泛型的定义,在函数中的使用。下面我们介绍在类属性中的泛型声明和使用。

示例:

代码语言:javascript复制
/**
 * 例如我扩展Arraylist的函数,声明返回第一个元素
 */
val <T> ArrayList<T>.first: T?
    get() = if (this.size > 1)
        this[0]
    else
        null

fun main(args: Array<String>) {
    //我们获取一个Arraylist 数组
    var s = arrayListOf("Z", "I", "N")
    print(s.first) //打印 Z
}

我们可以使用泛型在扩展接口中定义动态的参数数据。

3.声明泛型类和接口

其实泛型类和泛型接口这两个都差不多。我们了解接口和类的创建其实本质差不了太多。

而我们声明的泛型类和接口,其实最终还是落实在了类属性和函数中了。

只是将这个类的大量函数统一约束指定的泛型而已。

代码语言:javascript复制
class Zinyan<T> {
    var zin: T? = null

    fun test1(s: String): T? {
        println(s)
        return zin
    }

    fun test2(zz: T) {
        zin = zz
    }
}

fun main(args: Array<String>) {
    var s = Zinyan<String>()
    println(s.zin)
    println(s.test1("zinyan.com"))
    s.test2("z同学")
    println(s.zin)
}
//输出
null
zinyan.com
null
z同学

而在接口中定义泛型和类中的定义方式大同小异。

实例:

代码语言:javascript复制
expect interface RandomAccess<T>

expect inline fun <reified T> Array<out T>?.orEmpty(): Array<out T>
expect inline fun <reified T> Collection<T>.toTypedArray(): Array<T>

其他

更多的关于Kotlin方面的基础知识可以通过:Kotlin (zinyan.com)来了解。

里面包含了所有的本网站关于Kotlin的相关知识。

0 人点赞