泛型
函数和类的参数化类型
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++的模板)