一、泛型及泛型约束
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>