Musings on JDK development

  • Java
    March 15, 2010

Beware of Covariant Overriding in Interface Hierarchies

One of the changes to the Java programming language made back in JDK 5 was the introduction of
covariant returns, that is, the ability in a subtype to override a method in a supertype and return a more specific type. For example,

public class A {
public Object method() {return null;}
public class B extends A {
public String method() {return "";}

Covariant returns can be a very handy facility to more accurately convey the type of object returned by a method. However, the feature should be used judiciously, especially in interface hierarchies. In interface hierarchies, covariant returns force constraints on the implementation classes. Such a constraint was included the in the apt API modeling the Java language and was subsequently removed from the analogous portion of the standardized JSR 269 API in javax.lang.model.\*.

In apt, the
TypeDeclaration interface defines a method

Collection<? extends MethodDeclaration> getMethods().

In the sub-interface
ClassDeclaration, the method is overriden with

Collection<MethodDeclaration> getMethods()
and in another sub-interface,
AnnotationTypeDeclaration, the method is overriden with

Collection<AnnotationTypeElementDeclaration> getMethods().

Consequently, it is not possible for a single class to implement both the ClassDeclaration and AnnotationTypeDeclaration interfaces since the language specification forbids having two methods with the same name and argument types but different return types
(JLSv3 §8.4.2).
(This restriction does not exist at the class file level and a compiler will generate synthetic bridge methods with this property when implementing covariant returns.)
If a compiler chose to use a single type to model all kinds of types (classes, enums, interfaces, annotation types), it would not be able to be directly retrofitted to implement the entirely of this apt API; wrapper objects would need to be created just to allow the interfaces to be implemented at a source level.

In contrast, in JSR 269 the root modeling interface
Element defines a
List<? extends Element> getEnclosedElements() method which returns all kinds of enclosed elements, from fields, to constructors, to methods. Elements of a particular type can than be extracted using a filter. This approach provides more flexibility in retrofitting the interfaces onto an existing implementation; a spectrum of implementations are possible, from a single type to represent all sorts of elements to a one-to-one correspondence of implementation types to interface types.

Note that in cases where an implementation type collapses several interface types, instanceof checks for an interface type are not necessarily useful since implementing one interface does not imply no other related interface is implemented. The
Element specification warns of this possibility:

To implement operations based on the class of an Element object, either use a visitor or use the result of the getKind() method. Using instanceof is not necessarily a reliable idiom for determining the effective class of an object in this modeling hierarchy since an implementation may choose to have a single object implement multiple Element subinterfaces.

Join the discussion

Comments ( 2 )
  • Mohan Radhakrishnan Thursday, April 22, 2010

    The idea is that the two methods are covariant if there is a super-sub hierarchy ?

    Collection<? extends MethodDeclaration> getMethods().

    Collection<MethodDeclaration> getMethods()

  • Joe Darcy Thursday, April 22, 2010


    Yes, if an overriding method has a covariant return type, its return type is a subtype of the return type of the overridden method. Collection<MethodDeclaration> is a subtype of Collection<? extends MethodDeclaration>.

Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.