Kotlin 泛型

193 阅读3分钟

泛型(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泛型语法对应关系

javakotlin
? extends Tout
? super Tin
?*

小结:kotlin 和java 的泛型从功能来说都一样,也就语法不一样的而已,kotlin解决了java使用泛型的一些不合适之处。

作为个人的笔记用。