Search code examples
javainheritancechaining

Method chaining + inheritance 2 or more times


I wan't to be able to method chain multiple vector class that build on top of each other. I want to be able to extend that class.

Here is the 2d one:

public class Vec2 < C extends Vec2 > {

    public double x, y;

    public Vec2() {
    }

    public Vec2(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public C add(double x, double y) {
        this.x += x;
        this.y += y;
        return (C) this;
    }

}

This is for a vector with a z element.

class Vec3 < C extends Vec3 > extends Vec2<Vec3> {

    double z;

    public Vec3(){}

    public Vec3(double x, double y, double z) {
        super(x, y);
        this.z = z;
    }

    public C add(double x, double y, double z) {
        super.add(x, y);
        this.z  += z;
        return (C) this;
    }
}

But when is use a Vec3 then as soon as I use a method from Vec2 twice in a row then it returns a Vec2.

 Vec3<Vec3> a = new Vec3<>();
 // ------------------------------------------------->.add() in Vec2 cannot be aplied
 a.add(10, 20).add(10, 20, 10).add(10, 20).add(10, 20).add(10, 10, 20);

I don't want to write the class like this:

class Vec3 extends Vec2<Vec3> {

    // constructor etc. like before...

    public Vec3 add(double x, double y, double z) {
        super.add(x, y);
        this.z  += z;
        return this;
    }
}

Cause then when I make a Vec4 for example I have to Override every method in Vec3.

Is there a way (syntax) around this problem? Where no matter what It keeps returning the right class.


Solution

  • The problem is that you had a lot of "raw" type in your definitions, e.g.

    Vec2 < C extends Vec2 >
                     ----
                     raw type!
    

    After a few rounds, you'll reach a raw type, and erasure makes C the same as Vec2.


    We can do the following, using type variable This as the "self type"

    public class Vec2<This> {
    
        public This add(double x, double y) {
            this.x += x;
            this.y += y;
            return (This) this;
        }
    
    }
    
    public class Vec3<This> extends Vec2<This> {
    
        public This add(double x, double y, double z) {
            super.add(x, y);
            this.z  += z;
            return (This) this;
        }
    }
    
    public class Vec4<This> extends Vec3<This> {
    
    etc.
    

    But wait a second, how do we supply a This to Vec3?

        Vec3<Vec3<Vec3<......>>>    ???
    

    We can use a helper class

    public class V3 extends Vec3<V3>{}
    

    Now it all works; all add() methods in V3 return V3

        V3 a = new V3();
        a.add(10, 20).add(10, 20, 10).add(10, 20).add(10, 20).add(10, 10, 20);