Snakybo
Snakybo

Reputation: 429

Why is this an unchecked cast? And how to fix it?

I have the following code:

public final <T extends Component> T getComponent(Class<T> type) {
    Iterator<Component> it = components.iterator();

    while(it.hasNext()) {
        Component next = it.next();

        if(type.isAssignableFrom(next.getClass()))
            return (T)next; // Why is this an unchecked cast from Component to T?
    }

    return null;
}

And for some reason, return (T)next; is an unchecked cast from Component to T.

I'm not sure as to why this is, because T has to extend Component, it should be able to safely cast it to any subclasses of Component, right?

If I manually do return (TestComponent)next; and change the return type to TestComponent it works fine, so why doesn't it work with T?

Upvotes: 8

Views: 4060

Answers (3)

jtahlborn
jtahlborn

Reputation: 53694

Others have described the problem, here is the solution with cleaner test:

if (type.isInstance(next)) {
    return type.cast(next);
}

Upvotes: 15

Paul Boddington
Paul Boddington

Reputation: 37645

It's an unchecked cast because the compiler cannot be sure that next is a T. All it knows is that it's a Component.

As for your question about why casting to a T generates the warning, but not casting to a TestComponent, that's a lot more subtle. Casting to a TestComponent is inherently less dodgy than casting to a T. If test is not a TestComponent, the cast to a TestComponent would cause a ClassCastException at runtime. But this isn't the case for casting to a T because the type T is not known at runtime, due to type erasure. If you cast a Component that is not a T to a T and then add the result into a List<T>, you would have a List<T> where not all of the items are Ts. This would break the guarantee that generics are supposed to provide. There would be no chance of a ClassCastException preventing this.

In your case, you don't need to worry. You have checked the cast to a T is safe by passing the Class<T> object and doing the check. You have two choices. You could suppress the warning and add a comment explaining why it's safe to do so. However, a better alternative would be to write return type.cast(next); instead. This doesn't generate a warning because type.cast(object) would throw a ClassCastException if object were not a T.

Upvotes: 4

Dermot Blair
Dermot Blair

Reputation: 1620

As T is a subclass of Component, every T is a Component but not every Component is a T.

If a subclass inherits from a superclass, casting the superclass to the subclass cannot be performed successfully.

Therefore a new Component cannot be cast to a T instance.

Upvotes: 1

Related Questions