速懂contravariance, covariane (逆变、协变)

 前言

        英词成群,非求雅态。固知义足矣,译徒增负也。何以弃译知义,初篇答之。

正文

        刚才复习这个知识点,网上都说得很模糊,并且这个特性在很多种语言中都有,我便总结一下。为尽可能照顾广大读者,用Java和Kotlin(JVM)写了示例。示例很简单,如果某些语言特性不知道的话自己猜想一下便可以了,你猜的基本都是对的。

初衷

        先从初衷讲起,我认为所有教学都该这样。Covariance,contravariance是为了便于传递相似参数(和泛型不同)而设计的。Java中并不支持,但能用wildcard(通配符)来类似实现。

        先列出三个class,A为B的superclass,B为C的superclass。(superclass指父类,subclass指子类)

class A{
    int a = 0;
}

class B extends A {
    int b = 1;
}

class C extends B {
    int c = 2;
}

对于这样一个方法

void siteTest(List<B> list){...}

如果存在一个参数,其类型为List<C>或者List<A>, 它是传递不到siteTest这个方法中的,因为在siteTest中使用该参数时存在安全隐患(见下文)。但如果ide(开发工具)针对这种情况在写代码时加一些限制以确保安全,不就提高很多效率了吗。

Contravariance

        先用wildcard在Java中类似实现,如下图。

速懂contravariance, covariane (逆变、协变)

 如上,通过ide对list限制为只写,便安全了。

        如果List在设计中就可以一并实现的话,便是contravariance,如下

//interface换成class也是可以的
interface List<? super T>{}
//如果再限制T为A的subclass,便是
interface List<? super T extends A>{}

这样图中list的类型只要写为List<B>就可以了。当然这只是为了举例说明,Java的class或interface并不支持这样设计。不过很多其他语言都是支持的,如果用Kotlin来写的话便是

interface List<in T>
//或者
interface List<in T : A>

设计成in这个标识是为了更好帮你理解所限制的使用范围。这时ide会限制在List的子类中,T只能出现在方法括号的内部,T型变量也只能为private。试想一下,这样是不是就限制为只写了。

上述只是为了举例,Kotlin中的List并没有这样设计。

        此外,如果安全隐患可被自己手动排除呢。比如在写库的时候,自己明确知道会传进来哪些参数,可以使用哪些参数。如果list只可能为List<A>或者List<B>,那么使用list.get(0).a时是不是就安全了。(当然这是建立在已经确保list不为空,a已经初始化的情况下) Kotlin(JVM)为此提供了解决方法,对确保安全的参数,在其类型 T 前加一个注解UnsafeVariance,如下

class InSite <in T>(var value: @UnsafeVariance T)

此时value为private,但get,set方法为public。此外在方法中单独声明也是可以的,但这样就不能用@UnsafeVariance来解除限制了,如下:

fun inSiteTest(v:InSite<in B>){}

每种语言的解决方法不太一样,就不一一介绍了。

Covariance

先用wildcard在Java中类似实现,如下图。

速懂contravariance, covariane (逆变、协变)

 此时ide会在outSiteTest中对 list 限制为只读,排除安全隐患。在其他语言中实现covariance类比之前的contravariance,把 in 改为 out 即可,以限制T只能出现在方法的括号外部,内含的T型变量还是只能为private。

至此,已经把covariance和contravariance介绍完毕,相信你已经领悟了。

不定期更新

另有三个微信公众号,前者是针对整个计算机领域的,后两者是针对安卓的,并会转载第一个公众号的每篇文章。公众号可以根据需要分开关注,更新得也会快一些,欢迎浏览。

速懂contravariance, covariane (逆变、协变)

上一篇:vue 中的 provide/inject


下一篇:2021-10-16