Kotlin之泛型、泛型约束、协变(Variance)

一、泛型及泛型约束

kotlin中的泛型,和java中思维大体是相同的,但又有些区别

class Data<T>(val t:T)//泛型类
 fun <T> play(i:Int){ //泛型方法
    println(i)
 }

 interface onclick<T>{ //泛型接口
    fun click(t:T)
 } 


 val data = Data<String>("hello")//类实现

 val p = play<Int>(1) //方法实现

 class Data<T>(val t:T):onclick<T>{ //接口实现
    override fun click(t: T) {
        println(t)
    }
 }
//泛型约束 <占位符:类型>
fun <T:Number> play(vararg param: T):Double{
    return param.sumByDouble { it.toDouble() }
}

//多个约束,T有多个上限 , where T:类型,T:类型 
fun <T> getBetterBig(list:Array<T>,threhold:T):List<T> where T:Number,T:Comparable<T>{
    return list.filter { it>= threhold }.sorted()
}

用法和java没什么两样。。。

二、泛型协变

再看一个例子,三个简单的继承类

 open class C(open val name:String)
open class Java(override val name: String):C("java")
class Kotlin(override val name: String):Java("kotlin")

main

fun main(args: Array<String>) {
    val c = C("c")
    var cList:ArrayList<C> = arrayListOf(c)
    val java = Java("java")
    var javaList:ArrayList<Java> = arrayListOf(java)
    val kotlin = Kotlin("kotlin")
    var kotlinList:ArrayList<Kotlin> = arrayListOf(kotlin)
    for ((index,value) in cList.withIndex()) {
        println("$index - ${value.name}")
    }
    for ((index,value) in javaList.withIndex()) {
        println("$index - ${value.name}")
    }
    for ((index,value) in kotlinList.withIndex()) {
        println("$index - ${value.name}")
    }
}

打印结果

0 - c
0 - java
0 - kotlin

以上有3个ArrayList,类型分别是<C>,<Java>,<Kotlin>
每个list里放进了一个同类型的实例
结果没有任何问题

下面我们改一下代码

cList = javaList

马上编译报错

意思说虽然java是c的子类,但是ArrayList<Java> 可不是 ArrayList<C>的子类!

所以编译不能通过

java中用 ArrayList<? extends C>可以解决这个问题
在kotlin中要这么写

val c = C("c")
var cList:ArrayList<out C> = arrayListOf(c) // <out C>
val java = Java("java")
var javaList:ArrayList<Java> = arrayListOf(java)
cList = javaList
for ((index,value) in cList.withIndex()) {
        println("$index - ${value.name}")
}

打印结果

0 - java

这个时候,编译通过
其实这个写法和java的ArrayList<? extends C> 一模一样
<out C>就是允许C的子类,类型上限为C

来再改一下代码

val java = Java("java")
var javaList:ArrayList<Java> = arrayListOf(java)
val kotlin = Kotlin("kotlin")
var kotlinList:ArrayList<Kotlin> = arrayListOf(kotlin)
kotlinList = javaList //这次把javaList给装有子类的kotlinList 

同样出错了

同样提示说需要 ArrayList<Kotlin>类型 而只找到了 ArrayList<Java>的类型!

所以编译不能通过

再次改代码去应对

val java = Java("java")
var javaList:ArrayList<Java> = arrayListOf(java)
 val kotlin = Kotlin("kotlin")
 var kotlinList:ArrayList<in Kotlin> = arrayListOf(kotlin) // <in Kotlin>
 kotlinList = javaList
 for ((index,value) in kotlinList.withIndex()) {
        println("$index - ${value.name}")
  }

打印结果

0 - java

同理
kotlinList也能接受cList,因为C是Kotlin的超类
kotlinList = cList

打印结果

0 - c

这下可以赋值了,打印出来的结果是java,说明kotlinList里装了java的实例

其实这个写法和java的ArrayList<? super Kotlin> 一模一样
<in Kotlin>就是允许Kotlin的超类,类型下限为Kotlin

接下来要引用官文的两个概念了:生产和消费

interface Source<out T,in R>{
    fun 生产():T // 只能返回T,不能将T作为参数传入
    fun 消费(r:R) // 只能将R作为参数传入,不可返回R
}

生产 out -> 只出参
消费 in -> 只入参

作为<out T>的类型,由于所有类型均为T的下限,无法得知其确定的类型,所以不能使用set方法,只能get():T

还用上面的例子

val c = C("c")
var cList:ArrayList<C> = arrayListOf(c)
val java = Java("java")
var javaList:ArrayList<Java> = arrayListOf(java)
cList.add(java) //set
cList[0].name  //get

编译通过

将cList改成<out C>

val c = C("c")
var cList:ArrayList<out C> = arrayListOf(c) //<out C>
val java = Java("java")
var javaList:ArrayList<Java> = arrayListOf(java)
cList.add(java) //set 编译出错
cList[0].name  //get

prohibits(禁止) use of public open fun add(element:E) !

也就是说<out C>的类型 cList被禁止写入

接下来我们把<out C> 改为 <in C> 试试

val c = C("c")
    var cList:ArrayList<in C> = arrayListOf(c) //<in C>
    val java = Java("java")
    var javaList:ArrayList<Java> = arrayListOf(java)
    cList.add(java) //set
    cList[0].name  //get  编译出错

这个错误提示很少啊?为什么不是 prohibits use of ...?

其实这个错误并不是说<in C>的类型 cList不可以使用get读取,只是不知道cList[0]的类型而已!

三、星投影

但是如果我不知道类型该如何声明啊?

java中
private List list = new ArrayList();

在Kotlin中我也这么写

kotlin中
var list:ArrayList = ArrayList()

又是报错


Kotlin中必须这样写

var list:ArrayList<*> = arrayListOf(1) //<*>必不可少 相当于java的无泛型
当 cList:ArrayList<*> = javaList 时<*>相当于<out C>
当 javaList:ArrayList<*> = cList 时<*>相当于<in Java>
四、本章小节:
1、 Kotlin的泛型使用基本和Java一致
2、 Java的<? extends T> 相当于 Kotlin的<out T> ,Java的<? super T> 相当于 Kotlin的<in T>
3、<out T> 只能生产(出参), <in T> 只能消费(入参)
4、<out T> 只能生产的原因是编译器无法确认什么对象符合那个未知的 T 的子类型,只知道一定返回T,<in T> 只能消费的原因是无法确认T超类的具体类型
5、<*>相当于java中的无泛型。对于 Foo <out T>,其中 T 是一个具有上界的协变类型参数,Foo <*> 等价于 Foo <out Any>;对于 Foo <in T>,其中 T 是一个逆变类型参数,Foo <*> 等价于 Foo <in Nothing>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值