I have the following structure:
public interface BarReturn {}
public interface FooReturn {}
public interface FooBarReturn extends FooReturn, BarReturn {}
public interface Foo {
FooReturn fooBar( );
}
public interface Bar {
BarReturn fooBar();
}
public interface FooBar extends Foo, Bar {
FooBarReturn fooBar();
}
Javac fails with the following message:
FooBar.java:2: types Bar and Foo are incompatible; both define fooBar(), but with unrelated return types
public interface FooBar extends Foo, Bar {
^
1 error
However, Eclipse can compile it fine, and as far as I can see it should compile - FooBar's fooBar() method satisfies the contract of both Foo and Bar's fooBar() method by using covariant returns.
Is this a bug in the Eclipse compile or in javac? Or is there a way to persuade javac to compile it? For reference my javac options look like this:
javac -d /tmp/covariant/target/classes -sourcepath /tmp/covariant/src/main/java: /tmp/covariant/src/main/java/Foo.java /tmp/covariant/src/main/java/BarReturn.java /tmp/covariant/src/main/java/FooBarReturn.java /tmp/covariant/src/main/java/Bar.java /tmp/covariant/src/main/java/FooReturn.java /tmp/covariant/src/main/java/FooBar.java -g -nowarn -target 1.6 -source 1.6
The covariant return types are newly introduced since Java 5.0, and used during method overriding. Covariant return type allows us to change the return type of the overriding method in the subclass; however this return type in subclass method must be a subtype of super class method return type.
How is Covariant return types implemented? Java doesn't allow the return type-based overloading, but JVM always allows return type-based overloading. JVM uses the full signature of a method for lookup/resolution. Full signature means it includes return type in addition to argument types.
The covariant return typeA primitive type like int , float , reference , or void type can be the return value. As we said earlier, covariant return type means that a method's return type can be replaced with a narrower one when overridden in a subclass or child class.
We get the liberty to have more specific return types when overriding methods. Help in preventing run-time ClassCastExceptions on returns.
You are extending both Foo and Bar in your FooBar interface. As such you are inheriting two methods with incompatible return types. Java co-variance is only allowed when it follows Liskov substitution. Aka, the overriding candidate types must pretty much be a subclass of the overridden return type.
In your example above something like this should compile:
public interface BarReturn {}
public interface FooReturn {}
public interface FooBarReturn extends FooReturn, BarReturn {}
public interface Foo {
FooReturn fooBar( );
}
public interface FooBar extends Foo{
FooBarReturn fooBar();
}
The JLS (§9.4.1) says :
It is possible for an interface to inherit several methods with override-equivalent signatures (§8.4.2). Such a situation does not in itself cause a compile-time error. The interface is considered to inherit all the methods. However, one of the inherited methods must must be return type substitutable for any other inherited method; otherwise, a compile-time error occurs (The throws clauses do not cause errors in this case.)
So I would say that javac is right. But this looks like lawyer jargon to me, so I could be wrong.
An answer in this javaranch discussion seems to suggest that it's a javac bug. However, the referenced bug url doesn't seem to work.
I had the same problem and it seems to be fine by using the JDK 7 from Oracle.
As a workaround, you could do
interface Foo1 extends Foo {
FooBarReturn fooBar();
}
interface Bar1 extends Bar {
FooBarReturn fooBar();
}
public interface FooBar extends Foo1, Bar1 { }
Not pretty but should do the trick.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With