为什么带有参数list< object>的方法不能接受任何类型的列表,比如java中的list< a>作为参数?

eaf3rand  于 2021-07-05  发布在  Java
关注(0)|答案(1)|浏览(1053)

这个问题在这里已经有答案了

list是list的一个子类吗?为什么java泛型不是隐式多态的(19个答案)
4个月前关门了。
假设我们有一个方法 void m0(List<Object> lst) ,为什么我们不能用整数列表来调用它 List<Integer> iLst = new ArrayList(); m0(iLst); 使用方法时 void m0(Object a) , m0(1); 可以。从逻辑上讲,整数列表就是对象列表,为什么 m0(iLst); 不正确吗?

5anewei6

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 herevoid 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!

在上面的例子中,如果它已经编译并运行,那么最后一行将是一个错误,因为.get(0)调用将检索一个不是整数的双精度值。幸运的是,上面没有编译;错误发生在标记的行上。那是。。因为编译器应该禁止这样做。泛型本质上是不变的。
现在,协方差可以存在。例如,如果您有一个方法,它将汇总调用 .intValue() 在里面的每个数字上,你可以写:

public int sumAll(List<Number> list) {
   int result = 0;
   for (Number n : list) result += n.intValue();
   return result;
}

但这是一种糟糕的写作方式;您已经规定参数是不变的,因此,您不能传递 List<Integer> 对这件事。但是代码是协变的。如果您传递一个整数列表,它也同样有效。所以,你应该这样写 public int sumAll(List<? extends Number> numbers) 相反。
下面是不变性的一个例子:

public void addSumToEnd(List<Number> list) {
    int sum = 0;
    for (Number n : list) sum += n.intValue();
    list.add(sum);
}

因为我们在这里加了一个数字,所以你不能写 List<? extends Number> . 毕竟,我们正在添加一个 int 你不能这样对一个 List<Double> . 你可以在这里输入的唯一可接受的列表是 List<Number> 以及 List<Integer> 用java语言无法表达这一点。
对于列表,很简单:“相反=添加”( .add() , .addAll() ,等等),“协方差=读取”,“不变性=两者都做”。对于其他泛化类型,它可能没有那么简单。
如果你的 m0 方法类将只“读取”,然后您可以使其协变,并写入:

public m0(List<?> lst) {...}

以及 <?> 只是 <? extends Object> . 你拒绝给自己打电话 .add ,但你仍然可以打电话 .get ,最关键的是你能通过 List<String> 如果你读不懂 List<Object> (但是,另一方面,你可以打电话 .add()List<Object> 参数,并添加任何你喜欢的!

  • )这些是java中的实类型,但数字是抽象的。在本例中,假设不是,并且它们都没有args构造函数。关键是类型关系,而不是这些类型的任何特殊之处。

相关问题