泛型

函数和类的参数化类型

fun <T> foo(t: T)
class A<T>(t: T)

// 使用
foo(1) // 自动推断 T 为 Int
val a = A("") // 自动推断 T 为 String

协变与逆变

非泛型类型的子类型关系

open class Foo
class Bar : Foo()
val x: Foo = Bar() // OK

泛型类型的子类型关系

open class A
class B : A()
class Foo<T>

val x: Foo<A> = Foo<B>() // 错误

这里的子类型关系是指泛型类型Foo的实例之间的子类型关系。Foo<A>Foo<B> 是不同的类型,即使 A 和 B 之间存在子类型关系。

如果我们希望使用类的多态性来处理泛型类型的实例,我们需要引入协变和逆变的概念。

协变

关键字 out

val x: Foo<out A> = Foo<B>() // OK

将子类作为父类使用

x只能调用生产者方法,因为在调用x对应的方法时,事实上使用的是B中的方法,要求B作为参数类型,而A对应的子类型不一定为B

逆变

关键词 in

val x: Foo<in B> = Foo<A>()

将父类作为子类使用,此时x只能调用消费者方法,而且方法中的参数必须为子类型,相当于对参数做了更进一步的类型限制

此时生产者产生对象要求为子类型,但实际上为父类型,实际只能为Any?类型,相当于无法调用生产者方法

高级用法

泛型约束

class A<T : Number> // T 必须是 Number 的子类
class B<T> where T : Number, T : Comparable<T> // T 必须是 Number 的子类,且实现了 Comparable 接口
class C<out T> // T 只能用在生产者位置
class D<in T> // T 只能用在消费者位置
class E<out T: Number> // T 只能用在生产者位置,且必须是 Number 的子类
class E<in T: Number> // T 只能用在消费者位置,且必须是 Number 的子类

内联函数的泛型

内联函数不会发生类型擦除

同时可以使用关键字reified具体化类型参数,直接在函数中使用该类型(类似C++的模板)


电波交流