我的通用方法中的强制转换是否安全?

时间:2013-02-13 21:51:13

标签: java generics casting type-safety

我的项目中的代码如下所示:

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中的强制转换提供了警告,表明强制转换是不安全的。但是,我是否正确假设投射不会因为我对类型参数施加的限制而抛出,因此投射确实安全?

3 个答案:

答案 0 :(得分:6)

不一般。

假设Bar有方法void get(T value),并且Foo<String>MyFooYourFoo有两种实现方式。现在假设调用者在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>>,即UFoo<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。)