泛型(Genric Type) :参数化类型,可以用在
类,接口,函数。目的在于把类型可以像参数一样进行传递,可以提高代码复用,取消类型转换,减少运行时类型不匹配造成的异常。
kotlin 中有声明处型变(declare-site variance) 和 类型投影(Type Projections)
型变是什么
不得不说协变和逆变 概念性的东西总是令人迷惑
Kotlin中String类型都是继承自Any的,姑且记做String <= Any,表示String是Any的子类型,String的对象可以赋给Any的对象。
而Any 的泛型类型 List,我们可以理解成是由Any构造出来的一种新的类型,可以认为是一种构造类型,记作 F(Any) (可以类比下数学中函数的定义)
那么我们可以这么来描述协变和逆变:
-
当A ≦ B时,如果有f(A) ≦ f(B),那么f叫做协变;
-
当A ≦ B时,如果有f(B) ≦ f(A),那么f叫做逆变;
-
如果上面两种关系都不成立则叫做不可变。
协变和逆变表示的一种类型转变的关系:构造类型之间相对子类型之间的一种关系。
协变:保留子类型化关系
kotlin 中声明类在某个类型参数上是协变的,在该类型的参数上 加上out关键字。
如果A类是B类的子类,而且 Test<A>类 是Test<B>子类,那么这就是子类型化被保留了,这就是协变。
out 标记的泛型,只能作为返回值类型,作为生产者。
interface Test<out T>{
fun create():T
}
逆变:反转子类型化关系
kotlin 中声明类在某个类型参数上是逆变的,在该类型的参数上 加上in关键字。
如果A类是B类的子类,而且 Test<B>类 是Test<A>子类,与协变关系正好相反,这种关系称为逆变
interface Comaprator<int T>{
fun compare(a:T,b:T):Int
}
in 标记的泛型,只能作为传入进来的类型,是一种消费者。
使用处型变
Java中每一次使用带类型参数的类的时候(也就是泛型),可以指定这个类型参数为它的子类,或者父类 。kotlin 支持声明处和使用处型变。
fun <T> copy(source: MutableList<out T>,
destination: MutableList<T>){
for(item in source){
destination.add(item)
}
}
星号( * )投影:用*号代替类型参数
星号投影语法表示你不知道关于泛型实参的任何信息。MutableList<*> 和MutableList<Any?> 含义是不一样的。
泛型约束
kotlin中默认的上界(如果没有声明)是 Any?。在尖括号中只能指定一个上界。 如果同一类型参数需要多个上界,我们需要一个单独的 where-子句:
// 约束了T 必须是CharSequence接口和实现Comparable接口
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
java和kotlin泛型语法对应关系
| java | kotlin |
|---|---|
| ? extends T | out |
| ? super T | in |
| ? | * |
小结:kotlin 和java 的泛型从功能来说都一样,也就语法不一样的而已,kotlin解决了java使用泛型的一些不合适之处。
作为个人的笔记用。