5

我的项目中有如下代码:

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指出演员表不安全。但是,鉴于我对类型参数施加的限制,我是否正确地假设强制转换不会抛出,因此强制转换确实是安全的?

4

3 回答 3

6

一般不会。

假设Bar有一个方法,和void get(T value)有两种实现。现在假设调用者调用了 type 的值。这有效:当=时,我们有那是 的子类型。现在我们将该值转换为 a 。Foo<String>MyFooYourFooaddBarBar<MyFoo>UFoo<String>Bar<MyFoo>Bar<? extends U>Bar<Foo<String>>

现在如果Bar没有接受T's 作为参数的方法,那就没有问题了。但是假设它有一个方法void process(T value)。我们调用的实现有T= MyFoo,所以它只有一个process(MyFoo value)方法。但是,一旦我们将它转​​换为 a Bar<Foo<String>>,我们可能会用 aYourFoo来调用它。这是非法的。

在黑暗中刺伤,但我怀疑你真正想做的是声明barFooList<? extends Bar<? extends Foo<String>>.

于 2013-02-13T22:06:57.907 回答
2

这不是一个安全的演员阵容。日食是正确的。

想象一下,您有一个MyFoo扩展的类,并且当仅编译了签名时Foo,您传入了带有签名的Bar<MyFoo<String>>Now some 方法,因此方法查找将失败。BarmyMethod(Foo x)myMethod(MyFoo x)

于 2013-02-13T22:00:27.173 回答
0

演员表是不安全的因为虽然Uextends Foo<String>,它不是(必然)情况是Bar<U>extends Bar<Foo<String>>。事实上,只有当它们是相同的东西时Bar<U>才会扩展Bar<Foo<String>>,即 when Uis Foo<String>

直觉上,看起来(例如)List<String>应该是 的子类型List<Object>,但这不是泛型的工作方式。List<String>是 的子类型List<? extends Object>,但不是的子类型List<Object>。(考虑这样的例子可能更有意义Comparable<T>Comparable<String>表示“可以与任何比较String,而Comparable<Object>表示“可以与任何比较Object”。应该清楚,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;

unsafe,因为运行时无法检查强制转换(由于擦除),所以即使它是错误的,它也不会抛出异常:stringList现在是 a List<String>,其第一个元素是 type Integer。(发生的事情是,在稍后的某个时候,ClassCastException当您尝试使用该元素做某事时,您会变得自发。)

于 2013-02-13T22:08:20.460 回答