Why does Eclipse Compiler lose fixed type parameter?

216 views Asked by At

I struggled to find a proper title for this question because the phenomenon I observed is very strange. Hence I skip explaining my problem literally and instead show you some (hopefully) self-describing code. Consider the following parameterized class:

public class GenericOptional<T> {

    public GenericOptional(T someValue) {}

    public T getValue() { return null; }

    public Optional<String> getOptionalString() { return Optional.empty(); }
}

What I like to emphasize is that the return type Optional<String> of the method getOptionalString() does not depend on the type-parameter T.

Now have a look at the following code, which gets compiled inside Eclipse Luna 4.4.2 using Java 8u45:

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional go = (GenericOptional) obj;
    Optional os = go.getOptionalString();
}

The local variable os has the type Optional without the type-parameter String! The Eclipse compiler has lost the information about the fixed type-parameter. Does anyone know why?

Now look at a second code example:

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional<?> go = (GenericOptional) obj;
    Optional<String> os = go.getOptionalString();
}

By declaring the local variable go as GenericOptional<?> the return type of the method getOptionalString() now is Optional<String> as expected.

May anyone explain this behavior?

2

There are 2 answers

1
Holger On BEST ANSWER

You are facing the behavior of raw types. When you are using a raw type, Generics are effectively turned off completely, regardless of whether there is a connection between the generic signature of the member and the type parameter of the class.

The reasoning behind this is that raw types are a feature for backward compatibility with pre-Generics code only. So either you have Generics or your don’t.

If the Generic method does not depend on the actual type parameter of the class, the problem is easy to fix:

GenericOptional<?> go = (GenericOptional<?>) obj;
Optional<String> os = go.getOptionalString();

Using <?> implies “I don’t know the actual type parameter and I don’t care but I’m using Generic type checking”.

1
Konstantin Yovkov On

It's not about Eclipse or anything, but about raw types.

Let's review this snippet:

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional go = (GenericOptional) obj;
    Optional os = go.getOptionalString();
}

Here, you're creating a raw instance of GenericOptional, which means that the type-parameter information will be completely turned off. So, instantiating a raw GenericOptional means that the instance will expose the methods as following:

public class GenericOptional {

    public GenericOptional(Object someValue) {}

    public Object getValue() { return null; }

    public Optional getOptionalString() { return Optional.empty(); }
}

However, if we now review the second snippet

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional<?> go = (GenericOptional) obj;
    Optional<String> os = go.getOptionalString();
}

we can see that you're making a generic instance of GenericOptional. Even it's type-parameter is <?>, the compiler will not turn-off caring about type-parameters, so the instance will expose the getOptionalString() method parameterized, like this:

public Optional<String> getOptionalString() { return Optional.empty(); }