课程表

Kotlin课程

工具箱
速查手册

Kotlin 泛型

当前位置:免费教程 » 移动开发 » Kotlin

泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上。

与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。

声明一个泛型类:

  1. class Box<T>(t: T) {
  2. var value = t
  3. }

创建类的实例时我们需要指定类型参数:

  1. val box: Box<Int> = Box<Int>(1)
  2. // 或者
  3. val box = Box(1) // 编译器会进行类型推断,1 类型 Int,所以编译器知道我们说的是 Box<Int>。

以下实例向泛型类 Box 传入整型数据和字符串:

  1. class Box<T>(t : T) {
  2. var value = t
  3. }
  4.  
  5. fun main(args: Array<String>) {
  6. var boxInt = Box<Int>(10)
  7. var boxString = Box<String>("W3xue")
  8.  
  9. println(boxInt.value)
  10. println(boxString.value)
  11. }

尝试一下

输出结果为:

  1. 10
  2. W3xue

定义泛型类型变量,可以完整地写明类型参数,如果编译器可以自动推定类型参数,也可以省略类型参数。

Kotlin 泛型函数的声明与 Java 相同,类型参数要放在函数名的前面:

  1. fun <T> boxIn(value: T) = Box(value)
  2.  
  3. // 以下都是合法语句
  4. val box4 = boxIn<Int>(1)
  5. val box5 = boxIn(1) // 编译器会进行类型推断

在调用泛型函数时,如果可以推断出类型参数,可以省略泛型参数。

以下实例创建了泛型函数 doPrintln,函数根据传入的不同类型做相应处理:

  1. fun main(args: Array<String>) {
  2. val age = 23
  3. val name = "w3xue"
  4. val bool = true
  5.  
  6. doPrintln(age) // 整型
  7. doPrintln(name) // 字符串
  8. doPrintln(bool) // 布尔型
  9. }
  10.  
  11. fun <T> doPrintln(content: T) {
  12.  
  13. when (content) {
  14. is Int -> println("整型数字为 $content")
  15. is String -> println("字符串转换为大写:${content.toUpperCase()}")
  16. else -> println("T 不是整型,也不是字符串")
  17. }
  18. }

尝试一下

输出结果为:

  1. 整型数字为 23
  2. 字符串转换为大写:W3XUE
  3. T 不是整型,也不是字符串

泛型约束

我们可以使用泛型约束来设定一个给定参数允许使用的类型。

Kotlin 中使用 : 对泛型的的类型上限进行约束。

最常见的约束是上界(upper bound):

  1. fun <T : Comparable<T>> sort(list: List<T>) {
  2. // ……
  3. }

Comparable 的子类型可以替代 T。 例如:

  1. sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子类型
  2. sort(listOf(HashMap<Int, String>())) // 错误:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型

默认的上界是 Any?。

对于多个上界约束条件,可以用 where 子句:

  1. fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
  2. where T : Comparable, Cloneable {
  3. return list.filter(it > threshold).map(it.clone())
  4. }

型变

Kotlin 中没有通配符类型,它有两个其他的东西:声明处型变(declaration-site variance)与类型投影(type projections)。

声明处型变

声明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。

使用 out 使得一个类型参数协变,协变类型参数只能用作输出,可以作为返回值类型但是无法作为入参的类型:

  1. // 定义一个支持协变的类
  2. class W3xue<out A>(val a: A) {
  3. fun foo(): A {
  4. return a
  5. }
  6. }
  7.  
  8. fun main(args: Array<String>) {
  9. var strCo: W3xue<String> = W3xue("a")
  10. var anyCo: W3xue<Any> = W3xue<Any>("b")
  11. anyCo = strCo
  12. println(anyCo.foo()) // 输出 a
  13. }

in 使得一个类型参数逆变,逆变类型参数只能用作输入,可以作为入参的类型但是无法作为返回值的类型:

  1. // 定义一个支持逆变的类
  2. class W3xue<in A>(a: A) {
  3. fun foo(a: A) {
  4. }
  5. }
  6.  
  7. fun main(args: Array<String>) {
  8. var strDCo = W3xue("a")
  9. var anyDCo = W3xue<Any>("b")
  10. strDCo = anyDCo
  11. }

星号投射

有些时候, 你可能想表示你并不知道类型参数的任何信息, 但是仍然希望能够安全地使用它. 这里所谓"安全地使用"是指, 对泛型类型定义一个类型投射, 要求这个泛型类型的所有的实体实例, 都是这个投射的子类型。

对于这个问题, Kotlin 提供了一种语法, 称为 星号投射(star-projection):

  • 假如类型定义为 Foo<out T> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper ,Foo<> 等价于 Foo<out TUpper> . 它表示, 当 T 未知时, 你可以安全地从 Foo<> 中 读取TUpper 类型的值.
  • 假如类型定义为 Foo<in T> , 其中 T 是一个反向协变的类型参数, Foo<> 等价于 Foo<inNothing> . 它表示, 当 T 未知时, 你不能安全地向 Foo<> 写入 任何东西.
  • 假如类型定义为 Foo<T> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper , 对于读取值的场合, Foo<*> 等价于 Foo<out TUpper> , 对于写入值的场合, 等价于 Foo<in Nothing> .

如果一个泛型类型中存在多个类型参数, 那么每个类型参数都可以单独的投射. 比如, 如果类型定义为interface Function<in T, out U> , 那么可以出现以下几种星号投射:

  1. Function<*, String> , 代表 Function<in Nothing, String> ;
  2. Function<Int, *> , 代表 Function<Int, out Any?> ;
  3. Function<, > , 代表 Function<in Nothing, out Any?> .

注意: 星号投射与 Java 的原生类型(raw type)非常类似, 但可以安全使用

转载本站内容时,请务必注明来自W3xue,违者必究。
 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号