Question

I have code in my project that looks like this:

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 gives a warning for the cast in addBar that the cast is unsafe. However, am I correct in assuming that the cast will not throw given the restrictions that I have put on the type parameters, and therefore the cast is indeed safe?

Was it helpful?

Solution

Not in general.

Suppose Bar has a method void get(T value), and there are two implementations of Foo<String>, MyFoo and YourFoo. Now suppose a caller calls addBar on a value of type Bar<MyFoo>. This works: when U = Foo<String>, we have that Bar<MyFoo> is a subtype of Bar<? extends U>. Now we cast that value to a Bar<Foo<String>>.

Now if Bar has no methods that accept T's as arguments, there's no problem. But suppose it has a method void process(T value). The implementation we called has T = MyFoo, so it only has a process(MyFoo value) method. Once we cast it to a Bar<Foo<String>>, though, we might call it with a YourFoo instead. This is illegal.

Stab in the dark, but I suspect that what you really wanted to do was declare barFoo as a List<? extends Bar<? extends Foo<String>>.

OTHER TIPS

This is not a safe cast. Eclipse is correct.

Imagine you has a class MyFoo that extends Foo and you passed in a Bar<MyFoo<String>> Now some method in Bar with a myMethod(Foo x) signature when only a myMethod(MyFoo x) signature was compiled, so the method lookup would fail.

The cast is not safe, because although U extends Foo<String>, it is not (necessarily) the case that Bar<U> extends Bar<Foo<String>>. In fact, Bar<U> will only extend Bar<Foo<String>> when they are the same thing, i.e., when U is Foo<String>.

Intuitively, it may seem that (for example) List<String> should be a subtype of List<Object>, but this is not how generics work. List<String> is a subtype of List<? extends Object>, but it is not a subtype of List<Object>. (It may make more sense to consider an example like Comparable<T>: Comparable<String> means "can be compared to any String, whereas Comparable<Object> means "can be compared to any Object". It should be clear that Comparable<String> should not be a subtype of Comparable<Object>.)

[…] the cast will not throw […], and therefore the cast is indeed safe?

I think you're misunderstanding the nature of the warning. Eclipse is warning you that this cast will not throw even when it should, and this is actually why it's not safe. For example, this code:

final Object o = Integer.valueOf(7);
final String s = (String) o;

is perfectly safe, because the cast will throw an exception. But this code:

final List<?> wildcardList = new ArrayList<Integer>(Integer.valueOf(7));
final List<String> stringList = (List<String>) wildcardList;

is unsafe, because the runtime has no way of checking the cast (due to erasure), so it won't throw an exception, even though it's wrong: stringList is now a List<String> whose first element is of type Integer. (What happens is, at some point later on, you can get a spontaneous ClassCastException when you try to do something with that element.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top