在计算机编程中,泛型是为编程语言构造指定的参数类型。
在Java和Kotlin中,泛型可用于你在源代码中编写的类、函数和接口定义。
泛型在构架名称后面用角括号<> 。下面的例子显示了一个名为Car 的Kotlinclass ,它有一个泛型:
class Car<T>
上面的泛型<T> ,实际上是一个占位符类型,当你从类中实例化一个新的对象时,你可以用任何有效的Kotlin类型替换T 。
下面的例子都是有效的Car 实例,分别是String 和Int 类型:
val car = Car<String>()
val anotherCar = Car<Int>()
现在你知道了什么是泛型,接下来让我们学习一下为什么它对你的Kotlin应用程序有用。
在Kotlin中使用泛型的优势
一个用泛型定义的class ,可以让你创建一个更灵活的class ,比没有泛型的类能做更多的事情。
为了说明使用泛型的好处,设想你有一个Car ,如下图所示,它的主构造函数有一个name 参数:
class Car(val name: String)
现在,每次你创建一个Car 类的实例时,你总是会有一个name 类型的属性String
val car = Car("Tesla")
println(car.name) // Tesla
如果你试图在name 参数中传递一个Int ,在编译时就会出现如下错误:
val anotherCar = Car(2)
// The integer literal does not conform to the expected type String
由于class 参数需要有类型注释,你只能定义一种类型被class 接受。它要么是一个String 参数,要么是一个Int 参数。
这就是泛型可以帮助你的地方。通过为你的class 添加一个占位符类型,你可以在以后创建它的对象时定义你的class 参数的类型。
通过以下class 的定义,name 参数将具有你在实例化中定义的类型:
class Car<T>(val name: T)
现在,T 可以根据你的要求用String 或Int 来代替:
val car = Car<String>("Tesla")
println(car.name) // Tesla
val anotherCar = Car<Int>(2)
println(anotherCar.name) // Int
<T> 并不是Kotlin的任何特殊语法。它只是在定义泛型时使用的一种约定。
Kotlin泛型的约定如下:
E对于元素T为类型K为键V为值
你会看到上述约定在Kotlin官方文档中被广泛使用,如下:
fun <T> arrayListOf(): ArrayList<T>
// or
interface List<out E> : Collection<E>
此外,你可以在实例化过程中删除通用类型,让Kotlin从你传递的参数中推断出参数类型。
考虑一下下面的例子。在创建Box 对象时,省略了通用类型:
class Box<T>(val value: T)
val box = Box("Chocolate")
println(box.value) // Chocolate
val anotherBox = Box(9)
println(anotherBox.value) // 9
Kotlin中的一些class ,也实现了泛型,以帮助你编写一个灵活的应用程序。
例如,你可以在Kotlin中创建一个String 的数组,如下所示:
val stringArr = arrayOf<String>("London", "York", "Bristol")
但是由于Kotlin足够聪明,可以推断出你的数组的类型为<String> ,所以你可以在声明时将其删除:
val stringArr = arrayOf("London", "York", "Bristol")
最后,你可以为一个函数添加泛型,如下所示:
fun <T> getData(data: T) {
println(data.toString())
}
getData<Int>(5) // 5
总而言之,泛型允许你创建可以对不同类型的对象进行操作的类、函数或接口。
尽管泛型可以帮助你创建一个更灵活的程序,但使用泛型也会增加你的应用程序的复杂性。
只有当你知道你将需要一个单一class 的对象实例或一个需要对多种类型进行操作的函数时,你才应该使用泛型。
我的建议是,始终为你的Kotlin应用程序定义显式类型,只有在你的应用程序成长的过程中才进一步添加泛型。