CodeSculptor
CodeSculptor

Reputation: 65

Java 8 - Providing raw type argument to a method expecting generic type causes its return type to be erased

Following example compiles successfully in Java 7 and fails to compile in Java 8 (and newer).

public abstract class Example<T> 
{
    public T method() 
    {
        return method(new HashMap());
    }

    abstract T method(Map<String, String> arg);
}

Java 7:

BUILD SUCCESSFUL in 1s

Java 8:

> Task :compileJava FAILED
C:\dev\projects\Java8\src\main\java\example\Example.java:10: error: incompatible types: Object cannot be converted to T
        return method(new HashMap());
                     ^
  where T is a type-variable:
    T extends Object declared in class Example

Error above means that method(new HashMap()) returned Object instead of expected T.

To avoid this error in Java 8, I have to provide generic type argument i.e. change new HashMap() to new HashMap<>().

The disturbing part is that error caused by raw type argument passed to method(Map<String, String>) is actually about this method returning Object instead of T. So I can expect:

T result = method(new HashMap<>());

..., but:

Object result = method(new HashMap());

It doesn't seem to be intuitive behavior that parametrized type of method return is suddenly forgotten if non-generic type argument is provided where generic type argument is expected. It's just a context of argument in method definition that I'd expect to be isolated from context of return type in the same method's definition.

Is there a justification and applicable explanation for such behavior? I am aware of changes affecting generics done in Java 8, but nothing matching this specific case.

Thank you for your answers.

Upvotes: 2

Views: 537

Answers (2)

CodeSculptor
CodeSculptor

Reputation: 65

JLS, Chapter 15.

15.12.2.6. Method Result and Throws Types

The result type of the chosen method is determined as follows:

[...] if unchecked conversion was necessary for the method to be applicable, then the result type is the erasure (§4.6) of the method's declared return type. [...]

It got raised in JDK-6791481.

If I would make it public abstract class Example<T extends java.lang.Exception>, then I would get error: incompatible types: Exception cannot be converted to T.


(Again it seems that I have to fail to find an answer for a day, elaborate a long question, post it and find the answer shortly after.)

Upvotes: 2

Eugene
Eugene

Reputation: 120968

I can't really find the issue for java-7 (I'll try more), but it seems that this is specified in the JLS via:

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

I think it's this one, since I can't find anything under raw types that would better explain it. Or, in simple words, once you use raw types, everything else (even un-related stuff) will use erasure types as-well.

Upvotes: 1

Related Questions