在协变系统中,可以写入: Number x = new Integer(); 但你不能写: Integer y = new Number(); 正如您可能猜测的,java中的基本赋值等都是协变的。 有道理,对吧?不管我能做什么 Number 例如,我可以做一个 Integer 示例,例如调用 .intValue() 在上面。但反过来,它不成立; Integer 可能有方法 Number 不。 因此,正如您所熟悉的,基本的java赋值、参数传递等都是协变的。
逆变
在逆变系统中,不能写入: Number x = new Integer(); 但另一方面,这实际上是可行的: Integer y = new Number(); ###不变性 这是不灵活的;在这件事上,两者都不起作用。你唯一能做的就是: Integer y = new Integer(); ###好吧,那么,泛型呢? java对于基本的东西是协变的,而泛型则不是。泛型是逆变的、协变的或不变的,这取决于泛型的编写方式。 协变: List<? extends Number> list = new ArrayList<Integer>(); // legal 逆变: List<? super Integer> list = new ArrayList<Number>(); // legal 不变量: List<Integer> list = new ArrayList<Integer>(); // only integer will do here 与 void m0(List<Object> list) ,您选择了不变量。对于泛型部分,仅 <Object> 将做(和为 List 部分,它和“正常”java一样是协变的,所以 ArrayList<Object> 可以在这里传递,但是 List<String> 不能)。
嗯,wtf?为什么?
因为。。。生活。现实生活就是这样。 想象一下没有。我可以这样做,然后打破一切:
List<Integer> ints = new ArrayList<Integer>();
List<Number> numbers = ints; // MARK THIS LINE!
numbers.add(new Double(5.0));
Integer x = ints.get(0); // ERROR!
1条答案
按热度按时间5anewei61#
有一种叫做方差的东西。
让我们使用一些我们都熟悉的类型:
java.lang.Integer extends java.lang.Number extends java.lang.Object
*协方差
在协变系统中,可以写入:
Number x = new Integer();
但你不能写:Integer y = new Number();
正如您可能猜测的,java中的基本赋值等都是协变的。有道理,对吧?不管我能做什么
Number
例如,我可以做一个Integer
示例,例如调用.intValue()
在上面。但反过来,它不成立;Integer
可能有方法Number
不。因此,正如您所熟悉的,基本的java赋值、参数传递等都是协变的。
逆变
在逆变系统中,不能写入:
Number x = new Integer();
但另一方面,这实际上是可行的:Integer y = new Number();
###不变性这是不灵活的;在这件事上,两者都不起作用。你唯一能做的就是:
Integer y = new Integer();
###好吧,那么,泛型呢?java对于基本的东西是协变的,而泛型则不是。泛型是逆变的、协变的或不变的,这取决于泛型的编写方式。
协变:
List<? extends Number> list = new ArrayList<Integer>(); // legal
逆变:List<? super Integer> list = new ArrayList<Number>(); // legal
不变量:List<Integer> list = new ArrayList<Integer>(); // only integer will do here
与void m0(List<Object> list)
,您选择了不变量。对于泛型部分,仅<Object>
将做(和为List
部分,它和“正常”java一样是协变的,所以ArrayList<Object>
可以在这里传递,但是List<String>
不能)。嗯,wtf?为什么?
因为。。。生活。现实生活就是这样。
想象一下没有。我可以这样做,然后打破一切:
在上面的例子中,如果它已经编译并运行,那么最后一行将是一个错误,因为.get(0)调用将检索一个不是整数的双精度值。幸运的是,上面没有编译;错误发生在标记的行上。那是。。因为编译器应该禁止这样做。泛型本质上是不变的。
现在,协方差可以存在。例如,如果您有一个方法,它将汇总调用
.intValue()
在里面的每个数字上,你可以写:但这是一种糟糕的写作方式;您已经规定参数是不变的,因此,您不能传递
List<Integer>
对这件事。但是代码是协变的。如果您传递一个整数列表,它也同样有效。所以,你应该这样写public int sumAll(List<? extends Number> numbers)
相反。下面是不变性的一个例子:
因为我们在这里加了一个数字,所以你不能写
List<? extends Number>
. 毕竟,我们正在添加一个int
你不能这样对一个List<Double>
. 你可以在这里输入的唯一可接受的列表是List<Number>
以及List<Integer>
用java语言无法表达这一点。对于列表,很简单:“相反=添加”(
.add()
,.addAll()
,等等),“协方差=读取”,“不变性=两者都做”。对于其他泛化类型,它可能没有那么简单。如果你的
m0
方法类将只“读取”,然后您可以使其协变,并写入:以及
<?>
只是<? extends Object>
. 你拒绝给自己打电话.add
,但你仍然可以打电话.get
,最关键的是你能通过List<String>
如果你读不懂List<Object>
(但是,另一方面,你可以打电话.add()
在List<Object>
参数,并添加任何你喜欢的!