javagenericsreturn-type

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.


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.


UPDATE2: Now that I'm aware of it, I notice examples of this anti-pattern in open-source projects.


UPDATE3: I think I've found a requirement for generic methods that doesn't involve multiple parameters or a return type. Multiple inheritance would seem to be an exception to the above rule from Java's Tutorials.

<T extends Foo & Bar> void method(T foobar) {
    // Call Foo method.
    // Call Bar method.
}

Notably, the Collectors utility class includes generic methods like toList() that take no arguments. In this case the generic type only parameterizes the return type, which may be safer than the generic type actually being the return type.


Solution

  • 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;
    }