为什么名单<编号>不是一个子类型List
-
11-09-2019 - |
题
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中,List<S>
的不强> List<T>
的子类型时S
是T
的子类型。这条规则提供了类型安全。
让我们说,我们允许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协方差和我们阻止未(添加项目进入收集例如)类型安全操作。这样,我们得到的灵活性和类型安全。