Kotlin学习之泛型(协变及逆变)

首先看看在Java中的泛型

List<Object> list1;

这样声明,表示我可以给list1放置任何类型的对象。

List<String> list2;

这样表明,list2只可以放置类型是String的对象。

但是在java中下面这样情况是不允许的

List<String> list1 = new ArrayList();

List<Object> list2 = list1 ; //编译失败

这意味着 List<String> 并不是 List<Object> 的子类型

为什么呢?

原因是:假如说是允许的

List<String> list1 = new ArrayList();

List<Object> list2 = list1 ; //编译失败

list2.add(0);

String str = list2.get(0);//ClassCastException:无法将整数转换为字符串

Int类型转成字符串类型String,肯定是会类型转换异常的。

Java是不允许我们这样做的,那么为了解决这个问题,Java提供了通配符。

List<? extends Object> list;

表示我往List里面可以放置Object以及Object任意的子类型。

List<Object> list;

这个表示我只能放置Object类型。

他们是非常类似的,Java中所以的类型都是Object类型的。

接下里再看一个例子:实现把另外一个集合添加到当前的集合里面,

interface Collection<E> …… {
  void addAll(Collection<E> items);
}

一般情况下,Collection<E>的泛型是E类型的,那么addAll()里面添加进来的集合也应该是Collection<E>,这样是可以完美的添加进去。

那么看看这个方法能不能编译通过?

void copyAll(Collection<Object> to, Collection<String> from) {
  to.addAll(from);
}

 对于这种简单声明的 addAll 将不能编译:因为 Collection<String> 不是 Collection<Object> 的子类型,这样写是错误的。

实际Java中的addAll的声明是这样的

interface Collection<E> …… {
  void addAll(Collection<? extends E> items);
}

通配符类型参数 ? extends E 表示此方法接受 E 或者 E 的 一些子类型对象的集合,而不只是 E 自身。

可以看出 Collection<String>就是 Collection<? extends Object>的子类型

但是 Collection<String>绝对不是 Collection<Object>的子类型

这种表示形式限定了上界,这种情况称之为协变

接下里看下与之对应的逆变

List<? super String>

这里你只能调用接受 String 作为参数以及String层次体系上面的类型。

我们如果只从中读取数据,而不往里面写入内容,那么这样的对象叫生产者;如果只向里面写入数据,而不从中读取数据,那么这样的对象叫作消费者。

生产者使用?extends E  ;消费者使用?super  E

言归正传看看Kotlin如何解决协变和逆变相关的问题的

class MyClass<T>(t: T) {
    private var t: T
    init {
        this.t = t
    }
    fun get(): T = this.t
} 

fun myTest(myClass: MyClass<String>) {
    var myObject: MyClass<String> = myClass
    println(myObject.get())
 }
  
override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)

     var myClass = MyClass<String>("abc")
     myTest(myClass)
 }

Kotlin 中所有类型的父类是Any ,我对上面的程序做点改变

这样是不允许的,加个关键字out,就能正常执行

class MyClass<out T>(t: T) {
    private var t: T
    init {
        this.t = t
    }
    fun get(): T = this.t
}

这是为什么呢?

out表示只会读取,并不会修改这个值,就是协变

还有in

class MyClass<out T, M>(t: T, m: M) {
    private var t: T
    private var m:M
    init {
        this.t = t
        this.m = m
    }
    fun get(): T = this.t
    
    fun set(m:M){
        this.m = m
    }
}

 fun myTest(myClass: MyClass<String,Int>) {
     var myObject: MyClass<Any,Int> = myClass
     println(myObject.get())
 }

 override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)

     var myClass = MyClass<String,Int>("abc",20)
     myTest(myClass)
}

这样也可以正常运行

修改下程序 ,Int的父类是Number

加上in 可以解决问题

class MyClass<out T, in M>(t: T, m: M) {
    private var t: T
    private var m:M
    init {
        this.t = t
        this.m = m
    }
    fun get(): T = this.t
    fun set(m:M){
        this.m = m
    }
}

猜你喜欢

转载自blog.csdn.net/jingerlovexiaojie/article/details/106927400