Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected unchecked conversion warning

Tags:

java

generics

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 );
    }
}
like image 494
Konstantin Komissarchik Avatar asked May 01 '13 16:05

Konstantin Komissarchik


People also ask

How do I fix unchecked unchecked cast?

If we can't eliminate the “unchecked cast” warning and we're sure that the code provoking the warning is typesafe, we can suppress the warning using the SuppressWarnings(“unchecked”) annotation. When we use the @SuppressWarning(“unchecked”) annotation, we should always put it on the smallest scope possible.

How do you fix an unchecked conversion in Java?

We can use the annotation SuppressWarnings(“unchecked”) to suppress the warning.

What is unchecked warning?

An unchecked warning tells a programmer that a cast may cause a program to throw an exception somewhere else. Suppressing the warning with @SuppressWarnings("unchecked") tells the compiler that the programmer believes the code to be safe and won't cause unexpected exceptions.


2 Answers

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.

like image 194
Jon Skeet Avatar answered Oct 13 '22 01:10

Jon Skeet


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.

like image 26
Bhesh Gurung Avatar answered Oct 13 '22 01:10

Bhesh Gurung