简体   繁体   中英

Guarantee covariant return type with generics in Java

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

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 SpecialPoint s instead of Point s. 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?

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> { /* ... */ }

Possibly an interface Point solves your problem:

public class Test  
{

    public interface Point  {
        public Point[] neighbors();
    }

    public class SpecialPoint implements Point {
        public SpecialPoint[] neighbors() { return null; }
    }

    public class SpecialPoint2  implements Point {
        public SpecialPoint2[] neighbors() { return null; }
    }

    public Point doStuff(SpecialPoint point) {
        Point[] neighbors = point.neighbors();
        return neighbors[0];
    }

    public Point doStuff(SpecialPoint2 point) {
        Point[] neighbors = point.neighbors();
        return neighbors[0];
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM