Konstantin Komissarchik
Konstantin Komissarchik

Reputation: 29139

Unexpected unchecked conversion warning

Can anyone explain why there is an untyped conversion warning on y assignment line? Note that there is no warning on either x or z assignments.

public class Entity<T>
{
    @SuppressWarnings("unchecked")
    public <TX> Entity<TX> typed( Class<TX> type )
    {
        return (Entity<TX>) this;
    }

    @SuppressWarnings("unchecked")
    public static <TX> Entity<TX> typed( Entity<?> entity,  Class<TX> type )
    {
        return (Entity<TX>) entity;
    }

    public static void main( final String[] args )
    {
        final Entity<?> a = new Entity<Integer>();
        final Entity b = (Entity) a;

        final Entity<Integer> x = a.typed( Integer.class );
        final Entity<Integer> y = b.typed( Integer.class );
        final Entity<Integer> z = typed( b, Integer.class );
    }
}

Upvotes: 8

Views: 162

Answers (3)

Bhesh Gurung
Bhesh Gurung

Reputation: 51030

From the declaration:

final Entity<?> a = new Entity<Integer>();

a is typed, so the method call is a.typed( Integer.class ) is also typed.

In typed( b, Integer.class ) it works because the method is generic.

But in

final Entity b = (Entity) a;

you have turned-off generics (for b by using the raw-type instead of the generic-version of Entity) so the invocation b.typed( Integer.class ) is untyped. So you get the warning.

Upvotes: 1

cmbaxter
cmbaxter

Reputation: 35453

You are "downcasting" a, removing it's type identifier when assigning to b. Because b is now untyped, you are getting an untyped conversion warning because it no longer knows the type.

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1500913

b is of type Entity, which is a raw type. Therefore its API looks like this:

public Entity typed(Class type)

So you're converting from Entity to Entity<Integer>. The compiler has lost any correlation between the type parameter and the kind of entity returned, so it can't do any checking.

To put it another way, you could use:

final Entity<Integer> y = b.typed(String.class);

... and still receive only the same warning. If you try the same change with x or z, you'll get a compile-time error instead.

EDIT: As noted in comments, the fact that you're using a raw type removes all traces of generics.

From JLS section 4.8:

To facilitate interfacing with non-generic legacy code, it is possible to use as a type the erasure (§4.6) of a parameterized type (§4.5) or the erasure of an array type (§10.1) whose element type is a parameterized type. Such a type is called a raw type.

And then in section 4.6:

Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature s is a signature consisting of the same name as s and the erasures of all the formal parameter types given in s.

Upvotes: 6

Related Questions