JDK-6709429 : Allow overriding to resolve return type conflict
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Affected Version: 6
  • Priority: P5
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_vista
  • CPU: x86
  • Submitted: 2008-06-02
  • Updated: 2011-02-16
  • Resolved: 2008-07-25
Description
A DESCRIPTION OF THE REQUEST :
I'd like to define a type hierarchy for which a child overrides a method in two parents with different return types.  In general, this looks like:

interface A {}
interface B {}
interface C extends A, B {}

interface X { A foo(); }
interface Y { B foo(); }
interface Z extends X, Y { C foo(); }

Per JLS 8.4.8.4 (or perhaps an equivalent section describing constraints on interface methods rather than class abstract methods), this is illegal because no interface can extend both X and Y: A is not substitutable for B, nor vice versa.  However, it is impossible for any class that implements Z to violate type safety: foo() will always return an A, as required by X, and will always return a B, as required by Y.



JUSTIFICATION :
There are useful type hierarchies that can't be expressed without relaxing this restriction.  For example:

/** A set of pairs. */
interface Relation<T1, T2> {
  Relation<T2, T1> inverse();
}

/** A many-to-one relation. */
interface FunctionalRelation<T1, T2> extends Relation<T1, T2> {
  InjectiveRelation<T2, T1> inverse();
}

/** A one-to-many relation. */
interface InjectiveRelation<T1, T2> extends Relation<T1, T2> {
  FunctionalRelation<T2, T1> inverse();
}

interface OneToOneRelation<T1, T2>
extends FunctionalRelation<T1, T2>, InjectiveRelation<T1, T2> {
  OneToOneRelation<T2, T1> inverse();
}

On the flip side, I'm not aware of technical restrictions that make this difficult to allow.  It's just a matter of relaxing the overriding restriction.



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The compiler accepts declarations like that for OneToOneRelation without error.
ACTUAL -
javac produces an error:

types InjectiveRelation<T1,T2> and FunctionalRelation<T1,T2> are incompatible; both define inverse(), but with unrelated return types



---------- BEGIN SOURCE ----------
public class TestOverriding {
  interface Relation<T1, T2> {
    Relation<T2, T1> inverse();
  }
  interface FunctionalRelation<T1, T2> extends Relation<T1, T2> {
    InjectiveRelation<T2, T1> inverse();
  }
  interface InjectiveRelation<T1, T2> extends Relation<T1, T2> {
    FunctionalRelation<T2, T1> inverse();
  }
  interface OneToOneRelation<T1, T2>
      extends FunctionalRelation<T1, T2>, InjectiveRelation<T1, T2> {
    OneToOneRelation<T2, T1> inverse();
  }
}


---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The return type of one of the conflicting interfaces must be modified so that it is a supertype of the other, resulting in less precise typing (and probably leading to subsequent downcasting).  For example, InjectiveRelation could simply inherit Relation.inverse() with return type Relation<T2, T1>.

Comments
EVALUATION This code is valid. Z.foo overrides X.foo, and moreover Z.foo is return-type-substitutable for X.foo (since C is a subtype of A). Similarly for Y.foo. 8.4.8.4 is irrelevant, though not because it considers classes. We have an overriding situation here, and 8.4.8.4 talks about inheritance. If Z failed to override foo, then Z would be invalid whether X/Y/Z were interfaces or abstract classes; inheriting both 'A foo' and 'B foo' when they're not return-type-substitutable with each other is illegal by 8.4.8.4 AND 9.4.1. The fact that the code doesn't compile is a compiler bug; see 6294779, which is now fixed.
25-07-2008