为什么名单<编号>不是一个子类型List 的?

StackOverflow https://stackoverflow.com/questions/1240316

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

wahey(new LinkedList<Number>());

给此方法的调用将不是类型检查。我不能连铸参数如下:

wahey((List<Object>) new LinkedList<Number>());

从我的研究,我已经收集,对于不允许这样做的原因是类型安全。如果我们允许做上述情况,那么我们可以有以下几种:

List<Double> ld;
wahey(ld);

在方法wahey,我们可以一些字符串添加到输入列表(作为参数保持List<Object>参考)。现在,该方法调用之后,LD是指与类型List<Double>名单,但实际的列表包含一些字符串对象!

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

Object o;
Double d;
String s;

o = s;
d = (Double) o;

基本上我们在这里做的是同样的事情,但这次将通过编译时检查,只有在运行时出现故障。与解释的版本将不会编译。

这使我相信这纯粹是有关于仿制药的类型限制设计决策。我希望能得到这个决定一些意见?

有帮助吗?

解决方案

您在“没有泛型”例如正在做什么是一个演员,这清楚地表明,你正在做的事情型不安全。泛型等效将是:

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

o = s;
d.add((Double) o);

哪些行为相同的方式(编译,但在运行时失败)。之所以不让你问的行为是因为它允许的的类型不安全的行为,这是更难的通知代码。例如:

public void Foo(List<Object> list, Object obj) {
  list.add(obj);
}

这看起来完全正常和类型安全的,直到你怎么称呼它是这样的:

List<Double> list_d;
String s;

Foo(list_d, s);

这看起来也类型安全的,因为你作为主叫方不一定知道什么foo是要与它的参数做。

因此,在这种情况下,必须的代码两种看似类型安全位,它们一起最终被型不安全。这是不好的,因为它是隐藏的,因此难以避免和难以调试。

其他提示

考虑,如果它是...

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

您将能够父类的东西添加到列表中,这可能不是什么它以前宣布的,这正如上面的例子表明,引起各种问题。因此,它是不允许的。

它们不是相互仿制药因工作如何亚型。你想要的是来声明函数是这样的:

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

然后,它会接受任何东西列表扩展对象。你也可以这样做:

public void wahey(List<? extends Number> list) {}

这会让你的东西是数量的子类列出需要。

我建议你拿起“Java泛型和集合”的副本由莫里斯·纳夫特尔林与菲利普·沃德勒。

有基本上抽象二维这里,列表抽象和其内容的抽象。这是完全正常的沿列表抽象变化 - 说,例如,它是一个LinkedList或ArrayList的 - 但它不是罚款,以进一步限制的内容,说:这(名单持有的对象)是(链表这只拥有号码)。因为知道它是一种(列表保持的对象)了解到,由它的类型的合同,它可以容纳任何参考任何对象。

这是从你在非泛型的示例代码,在那里你说做了什么很大的不同:把这个字符串,就好像它是一个双。你是不是想说:对待这个(名单持有只数)为(名单持有任何东西)。它不,编译器可以检测到它,所以它不会让你逃脱它。

  

“我们正在做的事情本质上是   同样的事情,但这次将通过   编译时检查,只在失败   运行。与列出的版本不会   编译“。

什么您看到的是当你考虑到Java泛型的主要目的是让不兼容的类型在编译时,而不是运行时失败是非常合情合理的。

java.sun.com

  

泛型为你提供了一种   通信的集合的类型   编译器,因此它可以   检查。一旦编译器知道的   集合的元素类型,   编译器可以检查你所使用   收集一致且可   插入价值观正确铸件   取出来的集合。

在Java中,List<S> List<T>的子类型时ST的子类型。这条规则提供了类型安全。

让我们说,我们允许List<String>List<Object>的一个亚型。考虑下面的例子:

public void foo(List<Object> objects) {
    objects.add(new Integer(42));
}

List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);

因此,迫使这提供了类型安全,但它也有一个缺点,它的弹性较差。考虑下面的例子:

class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

在这里,你有T的集合,你想从另一个集合添加的所有项目。你不能与Collection<S>S亚型T调用此方法。理想情况下,这是可以的,因为你只添加元素融入到你的收藏,你是不是修改参数采集。

要解决这个问题,Java提供了他们所谓的“通配符”。通配符是提供协方差/逆变的一种方式。现在考虑使用通配符以下内容:

class MyCollection<T> {
     // Now we allow all types S that are a subtype of T
     public void addAll(Collection<? extends T> otherCollection) {
         ...

         otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
     }
} 

现在,用通配符我们允许在类型T协方差和我们阻止未(添加项目进入收集例如)类型安全操作。这样,我们得到的灵活性和类型安全。

scroll top