kotlin中的泛型
kotlin中的泛型
基本使用
class Box<T>(var t: T)
fun main() {
var box = Box(1)
println(box.t) // 1
}
声明处协变
kotlin中的out关键字叫做variance annotation
,因为是在类型声明处定义的,所以称为声明处协变。
对于java来说是使用处协变use-site variance
,其中类型通配符?
使得协变成为可能。
如果泛型类只是将泛型类型作为方法的输出值,也就是生产者,那么我们可以使用out,produce=output=out。
interface Producer<out T> {
fun produce() : T
}
如果泛型类只是将泛型类型作为方法的输入值,也就是消费者,那么我们可以使用in,consume=input=in。
interface Consumer<in T> {
fun consume(t: T)
}
如果泛型类同时将泛型类型作为方法的输入值和输出值,那么我们不能使用out和in关键字修饰泛型。
interface ProducerConsumer<T> {
fun produce() : T
fun consume(t: T)
}
使用处协变
假设现在需要将一个数组中的内容拷贝到另一个数组,浅拷贝,代码如下:
fun copy(from: Array<Any>, to: Array<Any>) {
assertEquals(from.size, to.size)
for (i in from.indices) {
to[i] = from[i]
}
}
然后调用这个方法:
fun main() {
var from: Array<Int> = arrayOf(1, 2, 3, 4)
var to: Array<Any> = Array(4) { "hello$it" }
copy(from, to) // Type mismatch
}
原因是Array<Int>
并不是Array<Any>
的子类,假设copy(from, to)
这一行编译通过,那么在copy方法内部就可以往from中添加String类型的元素,那么在main方法中从from取出Int类型的元素,就会抛出ClassCastException异常,代码应修改如下:
fun copy(from: Array<out Any>, to: Array<Any>) {...}
kotlin中同样支持in关键字用于使用处逆变,看下面的例子,设置Array某个位置的元素:
fun fill(dest: Array<String>, index: Int, value: String) {
dest[index] = value
}
使用如下:
var dest2: Array<Any> = Array(4) { "hello" }
fill(dest2, 2, "world") // Type mismatch
for(i in dest2.indices) {
println(dest2[i])
}
修改如下:
fun fill(dest: Array<in String>, index: Int, value: String) {...}
星投影
Star:如果T的上界是TUpper,那么Star<*>
就相当于Star<out TUpper>
,这表示当T的类型为未知时,可以从Star<*>中安全的取出TUpper类型的值。
class Star<out T>
fun main() {
var star = Star<Number>()
var star2: Star<*> = star
}
Star:如果T的上界是TUpper,那么Star<*>
就相当于Star<in Nothing>
,这表示你无法向其中写入数据。
class Star2<in T> {
fun setValue(t: T) {
}
}
fun main() {
var star3 = Star2<Number>()
var star4: Star2<*> = star3
// star4.setValue(3) // compile error
}
Star:如果T的上界是TUpper,那么Star<*>
就相当于读取时的Star<out TUpper>
以及写入时的Star<in Nothing>
。
class Star3<T>(var t: T) {
fun setValue(t: T) {
}
fun getValue(): T = this.t
}
fun main() {
var star5 = Star3<Number>(6)
var star6: Star3<*> = star5
star6.getValue()
// star6.setValue(2) // compile error
}
泛型函数
泛型除了可以用于类外,还可以用于函数。
fun <T> getValue(t: T) : T = t
fun main() {
println(getValue("hello"))
println(getValue(1024))
}
泛型的约束
约束泛型的父类为Comparable:
class UpperBounds<T: Comparable<T>>
fun main() {
var upperBounds = UpperBounds<String>()
}
约束泛型的父类为多个:
class UpperBounds2<T> where T: Comparable<T>, T: Number
泛型擦除
泛型其实就是一个语法糖,在编译之后会变成Any类型。
class MyStorage<out T>(private var t: T) {
fun getValue(): T = t
fun setValue(t: @UnsafeVariance T) {
this.t = t
}
}
fun main() {
var myStorage = MyStorage(50)
var myStorage2: MyStorage<Any> = myStorage
myStorage2.setValue("hello") // 泛型擦除
println(myStorage2.getValue())
}
上一篇: Java 运算符详情