Extends协变
带 extends 限定(上界)的通配符类型使得类型是 协变的(covariant),只能接受指定泛类型的子类型
定义为协变泛型时,实例化前无法确定子类型,泛型内部在实例化时使用实例类型作为容器类型,对该泛型实例对象无法写入存储实例类型的任何父类型,但可安全读取访问(右侧)其内部对象(总是可以安全地将泛型变量值转换为上界父类型)
只能 读取 的对象为 生产者,Kotlin中使用out
修饰符标记,并且该类型对象只能出现在输出(右侧)位置,类Source
则作为类型T
的生产者
协变参数类型在泛型中得到维持,在 Collection<out E>
定义中, Collection<Number>
是 Collection<Int>
的父类。
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // This is OK, since T is an out-parameter
// ...
}
Super逆变
带 super 限定(下界)的通配符类型使得类型是 逆变的(covariant),只能接受指定泛类型的父类型
定义为逆变泛型时,可知其最大子类型为Object / Any,泛型内部使用Object / Any类型作为容器类型,对该泛型对象的实例写入存储(左侧)是安全的(总是可以确定对象类型为容器的子类型,但访问时由于类型信息丢失,无法自动转换为其原本子类型)
只能 写入 的对象为 消费者,Kotlin中使用in
修饰符标记,并且该类型对象只能出现在输入(左侧)位置,类Comparable
则作为类型T
的消费者
abstract class Comparable<in T> {
abstract fun compareTo(other: T): Int
}
fun fill(dest: Array<in String>, value: String) {
// ...
}
不变
不变参数在泛型中,继承关系不被泛型维持,如在 MutableCollection<E>
定义中,MutableCollection<Number>
与 MutableCollection<Int>
不存在继承关系。
投影
指在实例化、作为参数类型声明时,对泛类型可接受变体(协变、逆变)的具体指定
fun copy(from: Array<out Any>, to: Array<Any>) {
// from 等同于 Array<? extends Object>
}
fun fill(dest: Array<in String>, value: String) {
// dest 等同于 Array<? super String>
}
星投影
指在实例化、参数类型声明时,使用*
代替具体的类型指定(表示任意类型)
Foo<*>
Function<*, String>
Function<*, *>
当泛型定义为<out T : TUpper>
时(T
为TUpper
的子类型),使用<*>
声明实例相当于声明为<out TUpper>
当泛型定义为<in T>
时,使用<*>
声明实例相当于声明为<in Nothing>
,该实例无法安全存储任何对象
当泛型定义为<T : TUpper>
时(T
类型为TUpper
),使用<*>
声明实例,在读取访问时相当于声明为<out TUpper>
,在写入存储时相当于声明为<in Nothing>
泛型约束
通常为指定上界约束
class Foo<out T : TUpper> {
// T 是上界为 TUpper 的子类型
// 并且作为生产者只能用于只读访问(使用 TUpper 作为访问接口类型)
}
fun <T : Comparable<T>> sort(list: List<T>) {
// 只有 Comparable<T> 的子类型可传递至该方法
}
sort(listOf(1, 2, 3)) // OK. Int is a subtype of Comparable<Int>
不指定上界时,默认为Any?
类型
当需要指定多于一个上界约束时,使用where
关键字
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
网友评论