Java Covariant Return Types produce Duplicate Methods

Categories: Java

I recently stumbled across a quirk of Java which is probably known to Java compiler writers, but I expect to not many otherwise. It would make a good question for really testing anybody who claims to be a Java guru..

Question : can a java class have two methods with the same name and parameters, but different return types?

Answer: In Java sourcecode, no. In JVM bytecode, yes. And there is a Java sourcecode feature that actually requires a compiler to generate such a class.

public interface ContentProvider {
  Object getContent();
}

public class StringProvider implements ContentProvider {
  public String getContent() {
    return "Hello, World!";
  }
}

The StringProvider class is using a “covariant return type” when implementing the getContent method - ie the method prototype declares the return-type as a subtype of the return-type on the overridden method signature. This feature was added in Java 1.5, and makes writing typesafe code somewhat easier.

In the JVM, runtime linking (ie for callers of methods, locating the real method to be called) is done using a “full” method signature : the “.class” file of the caller specifies (method-name, parameter-types, return-type) of the method to be invoked. Therefore sourcecode which invokes getContent() on a reference of type ContentProvider must generate bytecode that specifies the full signature of that method, including the return-type. And sourcecode which invokes getContent() on a reference of type StringProvider must produce bytecode that specifies a different signature.

It is therefore necessary that the bytecode generated for class StringProvider provide a matching method for each of these signatures - ie two methods with the same names, but different return types. The following output was generated by running “javap -classpath . StringProvider” against the compiled version of the above code, and shows the result.

Compiled from "StringProvider.java"
public class StringProvider implements ContentProvider {
  public StringProvider();
  public java.lang.String getContent();
  public java.lang.Object getContent();
}

The getContent method that returns an Object is auto-generated, and its implementation simply invokes the other getContent method that returns a String.