用代码实例解释Kotlin泛型

122 阅读3分钟

在计算机编程中,泛型是为编程语言构造指定的参数类型。

在Java和Kotlin中,泛型可用于你在源代码中编写的类、函数和接口定义。

泛型在构架名称后面用角括号<> 。下面的例子显示了一个名为Car 的Kotlinclass ,它有一个泛型:

class Car<T>

上面的泛型<T> ,实际上是一个占位符类型,当你从类中实例化一个新的对象时,你可以用任何有效的Kotlin类型替换T

下面的例子都是有效的Car 实例,分别是StringInt 类型:

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 可以根据你的要求用StringInt 来代替:

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应用程序定义显式类型,只有在你的应用程序成长的过程中才进一步添加泛型。