我的Kotlin 学习之路(七)Kotlin之泛型、泛型约束、协变(Variance)

一、泛型及泛型约束

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

class Data(val t:T)//泛型类


 fun  play(i:Int){ //泛型方法
    println(i)
 }

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


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

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

 class Data(val t:T):onclick{ //接口实现
    override fun click(t: T) {
        println(t)
    }
 }

//泛型约束 <占位符:类型>
fun  play(vararg param: T):Double{
    return param.sumByDouble { it.toDouble() }
}

//多个约束,T有多个上限 , where T:类型,T:类型 
fun  getBetterBig(list:Array,threhold:T):List where T:Number,T:Comparable{
    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) {

    val c = C("c")
    var cList:ArrayList = arrayListOf(c)

    val java = Java("java")
    var javaList:ArrayList = arrayListOf(java)

    val kotlin = Kotlin("kotlin")
    var kotlinList:ArrayList = 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,类型分别是,,
每个list里放进了一个同类型的实例
结果没有任何问题

下面我们改一下代码


cList = javaList

马上编译报错

我的Kotlin 学习之路(七)Kotlin之泛型、泛型约束、协变(Variance)_第1张图片
QQ图片20170704171452.png

**
意思说虽然java是c的子类,但是ArrayList 可不是 ArrayList的子类!
所以编译不能通过
**

java中用 ArrayList可以解决这个问题
在kotlin中要这么写


val c = C("c")
var cList:ArrayList = arrayListOf(c) // 

val java = Java("java")
var javaList:ArrayList = arrayListOf(java)

cList = javaList

for ((index,value) in cList.withIndex()) {
        println("$index - ${value.name}")
}

打印结果
0 - java

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

来再改一下代码

val java = Java("java")
var javaList:ArrayList = arrayListOf(java)

val kotlin = Kotlin("kotlin")
var kotlinList:ArrayList = arrayListOf(kotlin)

kotlinList = javaList //这次把javaList给装有子类的kotlinList 

同样出错了

我的Kotlin 学习之路(七)Kotlin之泛型、泛型约束、协变(Variance)_第2张图片
QQ图片20170704174145.png

**
同样提示说需要 ArrayList类型 而只找到了 ArrayList的类型!
所以编译不能通过
**

再次改代码去应对

val java = Java("java")
var javaList:ArrayList = arrayListOf(java)

 val kotlin = Kotlin("kotlin")
 var kotlinList:ArrayList = arrayListOf(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 一模一样
就是允许Kotlin的超类,类型下限为Kotlin

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


interface Source{

    fun 生产():T // 只能返回T,不能将T作为参数传入

    fun 消费(r:R) // 只能将R作为参数传入,不可返回R
       
}

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

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

还用上面的例子

val c = C("c")
var cList:ArrayList = arrayListOf(c)

val java = Java("java")
var javaList:ArrayList = arrayListOf(java)

cList.add(java) //set
cList[0].name  //get

编译通过

将cList改成


val c = C("c")
var cList:ArrayList = arrayListOf(c) //

val java = Java("java")
var javaList:ArrayList = arrayListOf(java)


cList.add(java) //set 编译出错
cList[0].name  //get

我的Kotlin 学习之路(七)Kotlin之泛型、泛型约束、协变(Variance)_第3张图片
QQ图片20170705104223.png

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

也就是说的类型 cList被禁止写入

接下来我们把 改为 试试

val c = C("c")
    var cList:ArrayList = arrayListOf(c) //

    val java = Java("java")
    var javaList:ArrayList = arrayListOf(java)


    cList.add(java) //set
    cList[0].name  //get  编译出错

我的Kotlin 学习之路(七)Kotlin之泛型、泛型约束、协变(Variance)_第4张图片
QQ图片20170705105533.png

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

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

三、星投影

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

java中
private List list = new ArrayList();

在Kotlin中我也这么写

kotlin中
var list:ArrayList = ArrayList()

又是报错

我的Kotlin 学习之路(七)Kotlin之泛型、泛型约束、协变(Variance)_第5张图片
QQ图片20170705134108.png


A){MX))Z{J74$DXF2PK}81F.gif

Kotlin中必须这样写


var list:ArrayList<*> = arrayListOf(1) //<*>必不可少 相当于java的无泛型

当 cList:ArrayList<*> = javaList 时<*>相当于
当 javaList:ArrayList<*> = cList 时<*>相当于

四、本章小节:

1、 Kotlin的泛型使用基本和Java一致
2、 Java的 相当于 Kotlin的 ,Java的 相当于 Kotlin的
3、 只能生产(出参), 只能消费(入参)
4、 只能生产的原因是编译器无法确认什么对象符合那个未知的 T 的子类型,只知道一定返回T, 只能消费的原因是无法确认T超类的具体类型
5、<*>相当于java中的无泛型。对于 Foo ,其中 T 是一个具有上界的协变类型参数,Foo <*> 等价于 Foo ;对于 Foo ,其中 T 是一个逆变类型参数,Foo <*> 等价于 Foo

你可能感兴趣的:(我的Kotlin 学习之路(七)Kotlin之泛型、泛型约束、协变(Variance))