我的项目中的代码如下所示:
public interface Bar<T extends Foo<?>> {
//...
}
public class MyFoo implements Foo<String> {
private List<Bar<Foo<String>> barFoo = ...
public <U extends Foo<String>> boolean addBar(Bar<? extends U> b) {
barFoo.add((Bar<Foo<String>>) b); //safe cast?
}
}
Eclipse为addBar
中的强制转换提供了警告,表明强制转换是不安全的。但是,我是否正确假设投射不会因为我对类型参数施加的限制而抛出,因此投射确实安全?
答案 0 :(得分:6)
不一般。
假设Bar
有方法void get(T value)
,并且Foo<String>
,MyFoo
和YourFoo
有两种实现方式。现在假设调用者在addBar
类型的值上调用Bar<MyFoo>
。这有效:当U
= Foo<String>
时,我们Bar<MyFoo>
是Bar<? extends U>
的子类型。现在我们将该值转换为Bar<Foo<String>>
。
现在,如果Bar
没有接受T
作为参数的方法,那么没有问题。但是假设它有一个方法void process(T value)
。我们调用的实现具有T
= MyFoo
,因此它只有process(MyFoo value)
方法。但是,一旦我们将其转换为Bar<Foo<String>>
,我们就可以用YourFoo
来代替它。这是非法的。
在黑暗中刺伤,但我怀疑你真正想做的是将barFoo
声明为List<? extends Bar<? extends Foo<String>>
。
答案 1 :(得分:2)
这不是一个安全的演员。 Eclipse是对的。
想象一下,您有一个MyFoo
扩展为Foo
的课程,并且您传递Bar<MyFoo<String>>
现在Bar
中的某个方法只有一个myMethod(Foo x)
签名编译了myMethod(MyFoo x)
签名,因此方法查找失败。
答案 2 :(得分:0)
演员不是安全的,因为虽然U
延伸Foo<String>
,但不(必然){{1} } extends Bar<U>
。事实上,Bar<Foo<String>>
只会在Bar<U>
相同的情况下展开Bar<Foo<String>>
,即U
为Foo<String>
时。{/ p>
直观地说,似乎(例如)List<String>
应该是List<Object>
的子类型,但这不是泛型的工作方式。 List<String>
是List<? extends Object>
的子类型,但不是 List<Object>
的子类型。 (考虑像Comparable<T>
这样的示例可能更有意义:Comparable<String>
表示“可以与任何String
进行比较,而Comparable<Object>
表示”可以与任何{Object
进行比较1}}“。应该清楚Comparable<String>
不应该是Comparable<Object>
的子类型。)
[...]演员不会抛出[...],因此演员确实安全吗?
我认为你误解了警告的性质。 Eclipse警告你,即使它,这个演员也不会抛出,这实际上是为什么它不安全。例如,此代码:
final Object o = Integer.valueOf(7);
final String s = (String) o;
非常安全,因为演员会抛出异常。但是这段代码:
final List<?> wildcardList = new ArrayList<Integer>(Integer.valueOf(7));
final List<String> stringList = (List<String>) wildcardList;
是不安全的,因为运行时无法检查强制转换(由于擦除),所以它不会抛出异常,即使它是错误的:stringList
现在是List<String>
,其第一个元素是Integer
类型。 (接下来会发生什么事情,当您尝试对该元素执行某些操作时,您可以获得自发的ClassCastException
。)