jaco0646 jaco0646 - 7 months ago 33
Java Question

Java compile error in method with generic return type

I can't explain why this simple code doesn't compile.

class Foo {
<T extends Foo> T method() {
return this;
}
}


The error is:
Type mismatch: cannot convert from Foo to T
.

Why not?
T
is defined to be
Foo
or its subclass.

While there are hundreds of Java generics questions on SO (and I've read a good number of them) they tend to involve long code samples and even longer explanations. I've not come across one this simple.

On a related note, an equivalent Scala example does compile, so the Java error is not due to a limitation of the JVM.

class Foo {
def method[T <: Foo] = this
}





UPDATE: I found a succinct piece of advice in the Java Tutorials regarding generic methods.


Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn't such a dependency, a generic method should not be used.


My interpretation is that a generic method is only appropriate in two scenarios.


  1. There is a dependency between two (or more) of the method's argument types.

  2. There is a dependency between the method's return type and one (or more) of its argument types.



Obviously, the simple code in question has no dependency among its arguments and return type, since there are no arguments. So a generic method is inappropriate.

Answer

I think you misunderstand how Java generics work:

<T extends Foo> T method() {

This means that the caller of that method can pick whatever subtype of Foo they want and ask for it. For example, you could write

Foo foo = new Foo();
SubFoo subfoo = foo.<SubFoo>method();

...and expect a SubFoo back, but your method implementation couldn't return a SubFoo, and this would have to fail. (I don't speak Scala, but I presume this means that your Scala implementation is not in fact "equivalent.")

If you want your method to be able to return a subtype of Foo that the implementation chooses, instead of the caller, then just write

Foo method() {
  return this;
}