Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Covariant Return Type in Interface not compiling via Javac

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
like image 417
Robert Elliot Avatar asked Aug 14 '11 11:08

Robert Elliot


People also ask

Does Java allow covariant return types?

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?

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.

Is covariant return type void?

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.

What is the best possible reason to use covariant return types?

We get the liberty to have more specific return types when overriding methods. Help in preventing run-time ClassCastExceptions on returns.


5 Answers

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();
}
like image 169
Perception Avatar answered Nov 02 '22 03:11

Perception


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.

like image 23
JB Nizet Avatar answered Nov 02 '22 02:11

JB Nizet


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.

like image 23
Don Roby Avatar answered Nov 02 '22 04:11

Don Roby


I had the same problem and it seems to be fine by using the JDK 7 from Oracle.

like image 26
Dr. Max Völkel Avatar answered Nov 02 '22 03:11

Dr. Max Völkel


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.

like image 1
hmakholm left over Monica Avatar answered Nov 02 '22 04:11

hmakholm left over Monica