2

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.

clankill3r
  • 9,146
  • 20
  • 70
  • 126
  • [This](http://stackoverflow.com/questions/1069528/method-chaining-inheritance-don-t-play-well-together), but I'm not sure how many levels it can go down. – Sotirios Delimanolis Jun 10 '15 at 18:03
  • I don't think you can go far, and this _Where no matter what It keeps returning the right class_ is pretty much impossible without developer involvement. – Sotirios Delimanolis Jun 10 '15 at 18:03

2 Answers2

2

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);
ZhongYu
  • 19,446
  • 5
  • 33
  • 61
  • 1
    @clankill3r - well, you cannot argue with that – ZhongYu Jun 10 '15 at 18:22
  • you are basically right, except that's not how you specify a *self-referencing* type. Use this instead: `Vec2 < C extends Vec2 >` and leave OP's code as-as (ie returning `C`) – Bohemian Jun 10 '15 at 18:26
  • @Bohemian - That's the popular practice, however I'm against it. The bound is complicated, confusing, and not needed/used anyway by the class body. And the call site cannot really use the bound to enforce that `C` is the "self-type". See http://stackoverflow.com/questions/30382847/avoiding-generic-types-of-form-fooactualtype-extends-fooactualtype/30656295#30656295 – ZhongYu Jun 10 '15 at 18:31
  • Therefore I propose that we simply adopt a conventional name, `This`, to represent this kind of type variable; forget the bound. – ZhongYu Jun 10 '15 at 18:36
0

You can't implement your code exactly as you intended, but the following compiles and I believe captures your intent:

interface Vec<C extends Vec<C>> {
    C add(C c);
}

class Vec2 implements Vec<Vec2> {
    public double x, y;
    public Vec2() {
    }

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

    public Vec2 add(Vec2 c) {
        this.x += c.x;
        this.y += c.y;
        return this;
    }
}

static class Vec3 implements Vec<Vec3> {
    Vec2 vec2;
    double z;
    public Vec3() {
        this(0,0,0);
    }

    public Vec3(double x, double y, double z) {
        vec2 = new Vec2(x, y);
        this.z = z;
    }

    public Vec3 add(Vec3 c) {
        vec2.add(new Vec2(c.vec2.x, c.vec2.y));
        this.z  += z;
        return this;
    }
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722