一篇文章了解Kotlin的泛型
Kotlin 泛型类型
Kotlin 的泛型特性允许我们编写出更加灵活和通用的代码,提高了代码的可重用性和类型安全性。
本文将介绍 Kotlin 中的四种泛型类型
- 类型参数
- 星号投影
- 型变
- 泛型限制
类型参数
定义一个泛型类或函数时,使用尖括号 < >
来指定类型参数。例如,以下是一个将泛型类型 T
用作参数的示例:
class MyList<T> { ... }
在这个示例中,T
是一个占位符类型参数,用于表示某个类型。在使用该类时,可以通过指定实际的类型参数来创建具体类型的实例。例如:
val list = MyList<String>()
在这个示例中,我们创建了一个 MyList
类型的实例,并将 String
类型指定为其类型参数。这意味着 list
变量可以存储 String
类型的元素。
星号投影
星号投影是一种特殊语法,用于表示您不关心实际类型参数的情况。通过使用 * 替代类型参数,您可以指定该参数将被忽略。例如,以下是一个使用星号投影的示例:
fun printList(list: List<*>) {
for (item in list) {
println(item)
}
}
在这个示例中,printList
函数接收一个 List<*>
类型的参数,该类型使用星号投影来表示它可以存储任何类型的元素。循环遍历该列表,并将每个元素输出到控制台。
型变
型变是指泛型类型之间的继承关系。在 Kotlin 中,有三种型变:in、out 和 invariant。这些型变用于描述子类型和超类型之间的关系,并影响如何将泛型类型赋值给其他类型。
in
型变:用于消费型位置(比如方法参数),表示只能从泛型类型中读取数据,不能写入数据。interface Source<out T> {
fun next(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs
// ...
}在这个示例中,我们定义了一个泛型接口
Source
,并使用out
关键字将其标记为协变类型。这意味着我们可以将Source<String>
类型的对象视为Source<Any>
类型的对象,并将其赋值给objects
变量。out
型变:用于生产型位置(比如返回值),表示只能向泛型类型中写入数据,不能读取数据。interface Sink<in T> {
fun put(element: T)
}
fun demo(sinkOfAny: Sink<Any>) {
val sinkOfString: Sink<String> = sinkOfAny
// ...
}在这个示例中,我们定义了一个泛型接口
Sink
,并使用in
关键字将其标记为逆变类型。这意味着我们可以将一个Sink<Any>
类型的对象视为Sink<String>
类型的对象,并将其赋值给sinkOfString
变量。invariant 型变:默认情况下,Kotlin 中的泛型类型都是不变(invariant)的。这意味着不能将一个
List<String>
类型的对象视为List<Any>
类型的对象。
泛型限制
泛型限制用于约束泛型类型可以具体化为哪些类型。例如,使用 where
关键字可以给泛型类型添加多个限制条件。以下是一个使用泛型限制的示例:
fun <T> showItems(list: List<T>) where T : CharSequence, T : Comparable<T> {
list.filter { it.length > 5 }.sorted().forEach(::println)
}
在这个示例中,我们定义了一个名为 showItems
的函数,它接受一个 List<T>
类型的参数,并对该列表进行过滤、排序和输出操作。其中,T
是一个泛型类型参数,用于表示列表中的元素类型。
为了限制 T
的类型,我们使用 where
关键字并添加了两个限制条件:T
必须实现 CharSequence
接口和 Comparable
接口。这意味着当我们调用 showItems
函数时,只能传递那些既实现了 CharSequence
接口又实现了 Comparable
接口的类型参数。
需要注意的是,在 Kotlin 中使用泛型限制时,限制条件必须放在 where
关键字之后,并且使用逗号 ,
分隔各个限制条件。如果有多个限制条件,建议将它们放在新行上,以提高代码的可读性。
链接:https://juejin.cn/post/7245194439785742396
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。