95

Kotlin 范型之泛型约束、类型投影、星号投影 - 简书

 4 years ago
source link: https://www.jianshu.com/p/ce9b093f6967?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
0.7652019.10.08 00:48:46字数 735阅读 1,185
webp
秋季的落叶.jpg

一. 泛型约束(Generic constraints)

Kotlin 跟 Java 一样,也拥有泛型约束。Java 使用 extends 关键字指明上界。

在 Kotlin 中使用:代替 extends 对泛型的的类型上界进行约束。

1.1 Upper bounds

下面的代码,在调用 sum() 函数时,传入的参数只能是 Number 及其子类,如果是其他类型,则会报错。

fun <T : Number> sum(vararg param: T) = param.sumByDouble { it.toDouble() }

fun main() {

    val result1 = sum(1,10,0.6)
    val result2 = sum(1,10,0.6,"kotlin") // compile error
}

Kotlin 默认的上界是Any?,为何是Any?而不是 Any 呢?

Any 类似于 Java 中的 Object,它是所有非空类型的超类型。但是 Any 不能保存 null 值,如果需要 null 作为变量的一部分,则需要使用Any?Any?是 Any 的超类型,所以 Kotlin 默认的上界是Any?

1.2 where 关键字

当一个类型参数指定多个约束时,在 Java 中使用&连接多个类、接口。

class ClassA { }

interface InterfaceB { }

public class MyClass<T extends ClassA & InterfaceB> {
    Class<T> variable;
}

而在 Kotlin 中,使用 where 关键字实现这个功能。下面的代码,T 必须继承 ClassA 以及实现 InterfaceB。

open class ClassA

interface InterfaceB

class MyClass<T>(var variable: Class<T>) where T : ClassA, T : InterfaceB

二. 类型投影(Type projections)

在上一篇文章<<Kotlin 范型之协变、逆变>>中,曾经介绍过 MutableList 是不变的,可读可写,没有使用 in、out 修饰。

如果对 MutableList 的参数类型使用 in 或者 out 修饰,会发生什么情况呢?

下面的代码说明了一切:

fun main() {
    val list1:MutableList<String> = mutableListOf()
    list1.add("hello")
    list1.add("world")

    val list2:MutableList<out String> = mutableListOf()
    list2.add("hello")  // compile error
    list2.add("world")  // compile error

    val list3:MutableList<in String> = mutableListOf()
    list3.add("hello")
    list3.add("world")

    lateinit var list4:MutableList<String>
    list4 = list3;     // compile error
}

使用 out 时,会报错,因为该参数只能出现在方法的返回类型。而使用 in 时,编译可以通过,因为该参数只能出现在方法的入参。

此时,list2 和 list3 分别表示一个受限制的 MutableList。在 Kotlin 中,这种行为被称之为类型投影。其主要作用是参数作限定,避免不安全操作。

正是由于 list3 是一个受限制的 MutableList,因此它赋值给 list4 报错也是可以理解了。

三.星号投影(Star-projections)

星号投影用来表明“不知道关于泛型实参的任何信息”。

类似于 Java 中的无界类型通配符?, Kotlin 使用星号投影*

*代指了所有类型,相当于Any?

例如:MutableList<*> 表示的是 MutableList<out Any?>

fun main() {
    val list1 = mutableListOf<String>()
    list1.add("string1")
    list1.add("string2")
    printList(list1)

    val list2 = mutableListOf<Int>()
    list2.add(123)
    list2.add(456)
    printList(list2)
}

fun printList(list: MutableList<*>) {

    println(list[0])
}

正是由于使用 out 修饰以及星号投影的类型不确定性,会导致写入的任何值都有可能跟原有的类型冲突。因此,星号投影不能写入,只能读取。

本文是 Kotlin 范型系列的最后一篇文章。
本文讲述了 Kotlin 泛型约束、类型投影、星号投影的特性。范型是 Kotlin 的高级特性,相对于 Java 的范型,它拥有更多的概念。

该系列的相关文章:
Kotlin 范型之协变、逆变
Kotlin 泛型之类型擦除


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK