Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guarantee covariant return type with generics in Java

Tags:

java

generics

I have a class called Point, with a method neighbors() that returns an array of Points:

public class Point {
    public Point[] neighbors() { /* implementation not shown */ }
}

I have a subclass of Point, called SpecialPoint that overrides neighbors() to return an array of SpecialPoints instead of Points. I think this is called covariant return types.

public class SpecialPoint extends Point {
    public SpecialPoint[] neighbors() { /* implementation not shown */ }
}

In a separate class, I want to make use of Point and SpecialPoint with generics

public <P extends Point> P doStuff(P point) {
    P[] neighbors = point.neighbors();
    // more stuff here including return
}

This will not compile, because the compiler can only guarantee that P is some subclass of Point, but there is no guarantee that every subclass of Point will override neighbors() to return an array of itself as I happen to have done with SpecialPoint, so Java only knows that P#neighbors() returns Point[], not P[].

How do I guarantee that each subclass overrides neighbors() with a covariant return type so I can use it with generics?

like image 591
JJW5432 Avatar asked Apr 16 '15 13:04

JJW5432


People also ask

What is the covariant return type in Java?

Covariant return type refers to return type of an overriding method. It allows to narrow down return type of an overridden method without any need to cast the type or check the return type. Covariant return type works only for non-primitive return types.

How is covariant return type implemented in Java?

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.

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

It helps to avoid confusing type casts present in the class hierarchy and thus making the code readable, usable and maintainable. We get the liberty to have more specific return types when overriding methods. Help in preventing run-time ClassCastExceptions on returns.

Is it possible to return a generic type in Java?

(Yes, this is legal code; see Java Generics: Generic type defined as return type only.) The return type will be inferred from the caller.


1 Answers

You may use an interface:

public interface Point<P extends Point<P>> {
    P[] neighbors();
}

public class SimplePoint implements Point<SimplePoint> {
    @Override
    public SimplePoint[] neighbors() { /* ... */ }
}

public class SpecialPoint implements Point<SpecialPoint> {
    @Override
    public SpecialPoint[] neighbors() { /* ... */ }
}

Then:

public <P extends Point<P>> P doStuff(P point) {
    P[] neighbors = point.neighbors();
    /* ... */
}

If you still need to factorize code between the implementations, then better use an abstract class:

public abstract class Point<P extends Point<P>> {
    public abstract P[] neighbors();
    public void commonMethod() { /* ... */ }
}

public class SimplePoint extends Point<SimplePoint> { /* ... */ }

public class SpecialPoint extends Point<SpecialPoint> { /* ... */ }
like image 103
sp00m Avatar answered Sep 17 '22 12:09

sp00m