Java 逆变与协变的名词说明

  最近在研究Thinking in Java的时候,感觉逆变与协变有点绕,特意整理一下,方便后人。我参考于Java中的逆变与协变,但是该作者整理的稍微有点过于概念化,我在这里简单的说一下

我对于协变于逆变的理解

一:协变

  协变返回类型指的是子类中的成员函数的不必严格等同于父类中被重写的成员函数的返回值类型,而可以是更 “狭窄” 的类型。当然协变也会出现在数据,泛型等地方。

1:协变的简单实例

  参考于 “理解Java中的协变返回类型”。 下边代码中,子类方法的返回值类型是父类方法返回值类型的子类型,这就是简单的协变示意。

java.io.ByteArrayInputStream;
java.io.InputStream;

Base
{
子类Derive将重写此方法,将返回类型设置为InputStream的子类
InputStream getInput()
{
  return System.in;
}
}
Derive Base
{

@Override
ByteArrayInputStream getInput()
{

return ByteArrayInputStream( byte[1024]);
}
main(String[] args)
{
Derive d= Derive();
System.out.println(d.getInput().getClass());
}
}
/*程序输出:
class java.io.ByteArrayInputStream
*/

2:数组使用协变

  数组支持协变, 比如 Parent [] pets =new Son[10] 。如果 son是parent的子类,那么这种定义形式在Java编译期是允许的。

但是,java中

   main(String[] args) {
        编译期不报错
        Object[] number =  Integer[10];
        number[0] = "123";
        System.out.println(number[0]);
    }
/**
结果:
Exception in thread "main" java.lang.ArrayStoreException: java.lang.String
    at com.generic.TestN.main(TestN.java:8)
**/

   main(String[] args) {
        下边就是数组类型的协变,感觉有点像是上转型
        Number[] number =  Integer[10];
        number[0] = 1;
        System.out.println(number[0]);
    }

3:数组不支持泛型,作为对比,容器支持泛型但不支持协变(不包括通配符)

 在这里 说一下 Java数组的特殊性,也是Java数组为什么敢使用协变的原因:

。虽然向上转型以后,编译期类型检查放松了,但因为数组运行时对内部元素类型看得紧,不匹配的类型还是插不进去的.

这也是为什么容器Collection不能设计成协变的原因。Collection不做运行时类型检查,比较耿直。所以的(当然,引入通配符之后这可以解决这一问题,我们待会在说)

List l =  ArrayList();
l.add("hello");
String str=l.get(0)
这里简单说一下擦除,上边是编译器的代码,运行时,泛型会被擦除

而且 ,java中数组明确规定

  Java Language Specification明确规定:

String[] s= String[]{"hello"};
int[] i= int[]{1,2,3};
Array的具体实现是在虚拟机层面,嵌地非常深,也查不到源码
只能用javap反编译看看具体初始化数组的字节码

下面是具体的反编译的字节码: 看注释说明,创建int数组和String数组的指令都不一样,换句话说,

Code:
0: iconst_1
1: anewarray #2 class java/lang/String
4: dup
5: iconst_0
6: ldc #3 String hello
8: aastore
9: astore_1
10: iconst_3
11: newarray int
13: dup
14: iconst_0
15: iconst_1
… …

4:泛型中的协变(带通配符的泛型)

run(List”running” cDogs = ArrayList tDogs = ArrayList
TianYuanQuan

二:逆变

   在Java中不允许将。泛型自然也不支持逆变。但是在泛型中可以

Test
{
main(String[] args)
{
List list = ArrayList();
}
}

  ? super Integer的含义是:支持Integer的父类,也包括Integer类,作为泛型的参数。

:http://www.linuxidc.com/Linux/2017-07/145628.htm