2021.8.26 带有主观性,理性观看,客观评价
WARNING:本文章适合了解一点协变和逆变但又不完全懂的人看一看
泛型协变 :如果定义了一个MyClass< T >的泛型类,其中A是B的子类型,同时MyClass< A >又是MyClass< B >的子类型,那么就可以称MyClass在T这个泛型上是协变的
错误写法:
open class Dad()
class Son(): Dad()
class Generics<out T> {
private var data: T?=null
fun get(): T?{
return data
}
fun set(t: T?){
data = t
}
}
fun function(generics: Generics<Dad>){
val dad = Dad()
generics.set(dad)
}
fun main(){
val sonGenerics = Generics<Son>()
function(sonGenerics)
}
正确写法:
open class Dad()
class Son(): Dad()
class Generics<out T> {
private var data: T?=null
fun get(): T?{
return data
}
}
fun function(generics: Generics<Dad>){
val dad: Dad? = generics.get()
}
fun main(){
val sonGenerics = Generics<Son>()
function(sonGenerics)
}
这边我定义了一个Generics泛型类,一个Dad父类,一个Son子类,先来说说为什么这样写是对的。 我这时候已经声明了T是只读的,那么不存在出现在in位置上的情况。 根据定义A是B的子类型,MyClass< A >就是是MyClass< B >的子类型,即使function需要的是一个泛型是Dad的Generics类,但Generics< Son >是Generics< Dad >的子类,就可以将参数传递进去,get方法就可以使用了,这个协变是很好理解的,就和多态差不多的道理。
注:generics.get()得到的是一个Son实例,但是被一个Dad类的dad引用接受了,这是完全可行的,因为多态,父类引用指向子类
再来说说错误写法错在哪,在这时候,我在Generics类中添加了set方法,如果我在function参数列表中传入一个Generics,这时候再调用它的set方法传入一个Dad实例,那么会出现类型转换异常(泛型指定Son而实例为Dad,Dad无法向下转型),这种行为是不安全的。
------------------------------------------分割线------------------------------------------
泛型逆变 :如果定义了一个MyClass< T >的泛型类,其中A是B的子类型,同时MyClass< B >又是MyClass< A >的子类型,那么我们就可以称MyClass在T上是逆变的
错误写法:
open class Dad()
class Son(): Dad()
class Generics<in T> {
private var data: T?=null
fun get(): T?{
return data
}
fun set(t: T?){
data = t
}
}
fun function(generics: Generics<Son>){
val value: Son? = generics.get()
}
fun main(){
val dadGenerics = Generics<Dad>()
function(dadGenerics)
}
正确写法:
open class Dad()
class Son(): Dad()
class Generics<in T> {
private var data: T?=null
fun set(t: T?){
data = t
}
}
fun function(generics: Generics<Son>){
val son = Son()
generics.set(son)
}
fun main(){
val dadGenerics = Generics<Dad>()
function(dadGenerics)
}
同样的我们先来看看为什么正确写法为什么正确,function指定的参数是Generics类型,那么这时传入一个Generics类型的参数,因为声明了in,T是只写的,所以根据定义A是B的子类型,MyClass< B >就是MyClass< A >的子类型,Generics传入function中,由于Generics是Generics的子类型,那么这个参数传入是正确的,function内部调用set方法,为传入的dadGenerics赋值,此时又可以联想到多态,在Generics中设置一个Son类型的实例,这是完全成立的,不会出现类型转换异常
那么错在哪?如果在function中调用get方法,那么由于实际传入的参数是Generics类型的,会获取到Dad实例,然而function中参数指定的是Generics类型,那么编译器就会认为get得到的就是一个Son类型的实例(我显式声明了),又因为Dad实例不能向下转型为Son实例,所以这样会导致类型转换异常。
至于为什么如此设计,暂时立个flag,以后会进行更新
由于作者没有先了解Java的协变和逆变直接就学了kotlin的协变和逆变,可能理解有偏差,请谅解
|