为什么List <Number>不是List <Object>的子类型?

Jac*_*ith 9 java generics casting covariance

public void wahey(List<Object> list) {}

wahey(new LinkedList<Number>());
Run Code Online (Sandbox Code Playgroud)

对方法的调用不会进行类型检查.我甚至无法将参数强制转换如下:

wahey((List<Object>) new LinkedList<Number>());
Run Code Online (Sandbox Code Playgroud)

根据我的研究,我收集到不允许这样做的原因是类型安全.如果允许我们执行上述操作,那么我们可以执行以下操作:

List<Double> ld;
wahey(ld);
Run Code Online (Sandbox Code Playgroud)

在方法wahey中,我们可以在输入列表中添加一些字符串(因为参数维护List<Object>引用).现在,在方法调用之后,ld引用一个带有类型的列表List<Double>,但实际列表包含一些String对象!

这似乎与没有泛型的Java正常工作方式不同.例如:

Object o;
Double d;
String s;

o = s;
d = (Double) o;
Run Code Online (Sandbox Code Playgroud)

我们在这里做的基本上是相同的,除了这将通过编译时检查,并且只在运行时失败.带有列表的版本将无法编译.

这让我相信这纯粹是关于泛型类型限制的设计决策.我希望对这个决定有所评论?

Tyl*_*nry 11

你在"没有泛型"的例子中所做的是一个演员,它清楚表明你正在做一些类型不安全的事情.与泛型相当的是:

Object o;
List<Double> d;
String s;

o = s;
d.add((Double) o);
Run Code Online (Sandbox Code Playgroud)

其行为方式相同(编译,但在运行时失败).不允许您询问的行为的原因是因为它允许隐式类型不安全操作,这在代码中更难注意到.例如:

public void Foo(List<Object> list, Object obj) {
  list.add(obj);
}
Run Code Online (Sandbox Code Playgroud)

这看起来非常好并且类型安全,直到你这样称呼:

List<Double> list_d;
String s;

Foo(list_d, s);
Run Code Online (Sandbox Code Playgroud)

这也看起来类型安全,因为你作为调用者并不一定知道Foo将如何处理它的参数.

因此,在这种情况下,您有两个看似类型安全的代码,这些代码最终都是类型不安全的.这很糟糕,因为它是隐藏的,因此难以避免并且难以调试.


Jam*_*mes 9

考虑一下......

List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException
Run Code Online (Sandbox Code Playgroud)

您可以将任何父类型的内容添加到列表中,这可能不是以前声明的内容,如上例所示,会导致各种问题.因此,不允许这样做.

  • 数组实际上允许这种类型的不安全行为。以下代码可以很好地编译,但是会产生运行时错误:`Object [] arr = new Integer [2]; arr [0] =“哦,不!”;` (2认同)