Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generic compile time error migrating from Java 6 to 7 or 8

We've started getting compile errors on code that used generics and that compiled successfully under Java 6. Here's a simple class to reproduce:

class Test {
    static class Foo<T> {
            T t;
            Foo(T t) { this.t = t; }
            T get() { return t; }
    }

    static class Bar extends Foo<Long> {
            Bar(Long t) { super(t); }
    }

    static class Foobar<N extends Number> extends Bar {
            Foobar() { super(5L); }
    }

    public static void main(String[] args) {
            Bar bar = new Bar(0L);
            Long b = bar.get();             // This works
            Foobar foobar = new Foobar();
            Long fb = foobar.get(); // This generates a compile time error
    }
}

The resulting error is:

Test.java:26: error: incompatible types: Object cannot be converted to Long
            Long fb = foobar.get(); // This generates a compile time error

Does anybody have any ideas?

like image 295
Michael van Rooyen Avatar asked Feb 12 '16 15:02

Michael van Rooyen


1 Answers

This is because Foobar without any type parameters is a raw type. Raw types have no generic abilities, so in this case the raw type Foobar extends Bar which extends the raw type Foo. Raw types use the upper bound of their parameters, as they are compiled that way after erasure and the compiler has no type parameters to safely insert casts.

In the case of Foo, this upper bound is Object. This causes Foo.get() to return Object, so Bar.get() returns Object, and so Foobar.get() returns Object as well. Obviously, the compiler won't cast Object to Long without an explicit cast.

In contrast, the parameterized type Foobar<N> extends Bar which extends the parameterized type Foo<Long>. The compiler can now use generics, so it sees that Foo<T>.get() has type T, that Foo<Long>.get() has type Long, that Bar.get() has type Long, and finally that Foobar<N>.get() has type Long as well.

This means that you should declare foobar as shown:

Foobar<...> foobar = new Foobar<...>();

It doesn't matter what the type parameter of foobar is, just as long as it is present. It can even be the wildcard ?.

like image 135
HTNW Avatar answered Sep 28 '22 01:09

HTNW