首先看看在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
}
}