前言
详细介绍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
的相关知识。