meliniak
meliniak

Reputation: 772

Generic behaviour

(sorry for the pun)

Say one wants to define a generic builder, like this:

public abstract class GenericBuilder<T extends Product> {
    int x;
    int y;

    <K extends GenericBuilder<T>> K setX(int x) {
        this.x = x;
        return (K)this;
    }

    <K extends GenericBuilder<T>> K  setY(int y) {
        this.y = y;
        return (K) this;
    }

    abstract T build();
}

abstract class Product {
    int x;
    int y;
}

class ConcreteProduct extends Product {
    int z;
}

class ConcreteBuilder extends GenericBuilder<ConcreteProduct>{
    int z;

    <K extends GenericBuilder<ConcreteProduct>> K  setZ(int z) {
        this.z = z;
        return (K) this;
    }

    @Override
    ConcreteProduct build() {
        ConcreteProduct cp = new ConcreteProduct();
        cp.x = x;
        cp.y = y;
        cp.z = z;
        return cp;
    }

    public static void main(String[] args) {
        new ConcreteBuilder().setX(1).setY(2).setZ(3);
    }
}

When calling ConcreteBuilder.setZ(), it fails during compilation.

Why is that? Is it due erasure? Or the generics, say, don't carry information about its generic parameters?

EDIT:

Any ideas how to avoid using second generic parameter in:

public class ConcreteBuilder extends GenericBuilder<ConcreteProduct, ConcreteBuilder>

i.e. <..., ConcreteBuilder>, which seems to be a little clumsy? I guess it's not possible. Are there other languages (C# maybe?) which allow to do that?

Upvotes: 0

Views: 131

Answers (2)

fabian
fabian

Reputation: 82461

In your GenericBuilder your functions return a GenericBuilder when you don't specify the type argument of the function. In your main function the call to setX returns a GenericBuilder and you loose the information that you are actually using a ConcreteBuilder. To succesfully make the calls, you have to specify the generic parameters for the setters:

new ConcreteBuilder().<ConcreteBuilder>setX(1).<ConcreteBuilder>setY(2).setZ(3);

Alternative

You can add a second type parameter to GenericBuilder:

public abstract class GenericBuilder<T extends Product, K extends GenericBuilder<T, K>> {
    int x;
    int y;

    K setX(int x) {
        this.x = x;
        return (K)this;
    }

    K  setY(int y) {
        this.y = y;
        return (K) this;
    }

    abstract T build();
}

and change ConcreteBuilder to this:

public class ConcreteBuilder extends GenericBuilder<ConcreteProduct, ConcreteBuilder> {
    int z;

    ConcreteBuilder setZ(int z) {
        this.z = z;
        return this;
    }

    @Override
    public ConcreteProduct build() {
        ConcreteProduct cp = new ConcreteProduct();
        cp.x = x;
        cp.y = y;
        cp.z = z;
        return cp;
    }

    public static void main(String[] args) {
        new ConcreteBuilder().setX(1).setY(2).setZ(3);
    }
}

Upvotes: 2

Kalher
Kalher

Reputation: 3653

Break your code this way and you will understand that your class GenericBuilder<ConcreteProduct> doesn't have any setZ() method defined.

    GenericBuilder<ConcreteProduct> setY = new ConcreteBuilder().setX(1).setY(2);
    setY.setZ(3);

Upvotes: 2

Related Questions