我的Kotlin 学习之路(七)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

马上编译报错

QQ图片20170704171452.png

**
意思说虽然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 

同样出错了

QQ图片20170704174145.png

**
同样提示说需要 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

QQ图片20170705104223.png

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  编译出错

QQ图片20170705105533.png

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

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

三、星投影

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

java中
private List list = new ArrayList();

在Kotlin中我也这么写

kotlin中
var list:ArrayList = ArrayList()

又是报错

QQ图片20170705134108.png


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

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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容