Reputation: 65
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
Reputation: 65
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
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