I can override a method with covariant return type, but is it possible to override a default method with covariant return type? In the following example, I would like to override getFirstLeg without rewriting the default method, but Java does not allow that. I also do not want to make Animal a generic interface because there may be many parameters and Animal is used in a lot of places.
interface Leg {
}
interface Animal {
List<? extends Leg> getLegs();
default Leg getFirstLeg() {
return getLegs().get(0);
}
}
abstract class AnimalImpl<T extends Leg> implements Animal {
private List<T> legs;
@Override
public List<T> getLegs() {
return legs;
}
}
interface DuckLeg extends Leg {
}
interface Duck extends Animal {
@Override
List<? extends DuckLeg> getLegs(); //covariant return type
@Override
DuckLeg getFirstLeg(); //I do not want to rewrite this method
}
class DuckImpl extends AnimalImpl<DuckLeg> implements Duck {
//Error: java: DuckImpl is not abstract and does not override abstract method getFirstLeg() in Duck
}
Update: The following code compiles, but the new problem is that Duck is no longer an Animal.
interface Leg {
}
interface AnimalGeneric<T extends Leg> {
List<? extends T> getLegs();
default T getFirstLeg() {
return getLegs().get(0);
}
}
abstract class AnimalImpl<T extends Leg> implements AnimalGeneric<T> {
private List<T> legs;
@Override
public List<T> getLegs() {
return legs;
}
}
interface Animal extends AnimalGeneric<Leg> {
//empty
}
interface BirdLeg extends Leg {
}
interface BirdGeneric<T extends BirdLeg> extends AnimalGeneric<T> {
}
class BirdImpl<T extends BirdLeg> extends AnimalImpl<T> implements BirdGeneric<T> {
}
interface Bird extends BirdGeneric<BirdLeg> {
//empty
}
interface DuckLeg extends BirdLeg {
}
class DuckImpl extends BirdImpl<DuckLeg> implements Duck {
}
interface Duck extends BirdGeneric<DuckLeg> {
}
Yes, Java does have covariant return types for inherited methods. But the problem here isn't the fact that
getFirstLegis adefaultmethod, it's that Java's generics are not covariant; they're invariant. I.e. AList<DuckLeg>is not aList<? extends Leg>, even if aDuckLegis aLeg.But you can get around this by making the
Animalinterface generic, so that the type parameter can change based on the sub-interface(s).Then
Duckhas a type parameter also, but does not need to override anything anymore. The type argumentDuckLegis assigned toT.Then your
DuckImplclass will inherit everything properly, and you won't get the "not abstract and does not override abstract method" error.Of course with an empty declaration of
Duck, you may not need it at all. Just haveDuckImplimplementAnimal<DuckLeg>.