0

I am creating DTO structure with Builder pattern. Because of existence of many requests I created parent request AbstractRequest to create concrete requests - e.g. ConcreteRequest in this example.

Base Buildable interface defines contract to all Requests.

public interface Buildable<T> {

    T build();

    void validate();

}

Parent request AbstractRequest to create concrete ConcreteRequest that holds parameters used by all descendants (for brevity globalValue only in this example).

public abstract class AbstractRequest {

    private final String globalValue;

    public AbstractRequest(BuilderImpl builder) {
        this.globalValue = builder.global;
    }

    public interface Builder<T> extends Buildable<T> {

        Builder<T> globalValue(String globalValue);

    }

    public abstract static class BuilderImpl<T> implements Builder<T> {

        private String global;

        @Override
        public Builder<T> globalValue(String globalValue) {
            this.global = globalValue;
            return this;
        }

    }

}

Concrete request that has one private parameter localValue:

public final class ConcreteRequest extends AbstractRequest {

    private final String localValue;

    public ConcreteRequest(BuilderImpl builder) {
        super(builder);
        this.localValue = builder.localValue;
    }

    public String getLocalValue() {
        return localValue;
    }

    public static Builder builder(){
        return new BuilderImpl();
    }

    public interface Builder extends AbstractRequest.Builder<ConcreteRequest> {

        Builder localValue(String localValue);

    }

    public static final class BuilderImpl extends AbstractRequest.BuilderImpl<ConcreteRequest> implements Builder {

        private String localValue;

        @Override
        public ConcreteRequest build() {
            this.validate();
            return new ConcreteRequest(this);
        }

        @Override
        public void validate() {
            // do validation
        }

        @Override
        public Builder localValue(String localValue) {
            this.localValue = localValue;
            return this;
        }
    }

}

Q: Why is not ConcreteRequest#getLocalValue accessible while ConcreteRequest#build is available?

enter image description here

jnemecz
  • 3,171
  • 8
  • 41
  • 77
  • The `globalValue()` method returns a `Builder`, which does not have a `getLocalValue()` method. `getLocalValue()` is a method of `ConcreteRequest`. `Builder` does, however, have a `build()` method. So your assertion that `ConcreteRequest#build()` is available is actually incorrect, the method you are seeing is `Builder#build()`. – Charlie Armstrong Dec 02 '22 at 16:40
  • you're also implementing `Builder` _and_ `Builder`. Careful of rawtypes. – Rogue Dec 02 '22 at 16:48
  • @CharlieArmstrong Please, could you check my answer if is it correct? – jnemecz Dec 02 '22 at 18:02
  • @Rogue Please, could you check my answer if is it correct? – jnemecz Dec 02 '22 at 18:02

1 Answers1

0

I modified my code and it seems it works.

public interface Buildable<T> {

    T build();

    void validate();

}

Parent class:

public abstract class AbstractRequest {

    private final String globalValue;

    public AbstractRequest(BuilderImpl builder) {
        this.globalValue = builder.global;
    }

    public String getGlobalValue() {
        return globalValue;
    }

    public interface Builder<B extends Builder, C extends AbstractRequest> extends Buildable<C> {

        B globalValue(String globalValue);

    }

    public abstract static class BuilderImpl<B extends Builder, C extends AbstractRequest> implements Builder<B, C> {

        private String global;

        @Override
        public B globalValue(String globalValue) {
            this.global = globalValue;
            return (B) this;
        }

        @Override
        public void validate() {
            if (global == null) {
                throw new IllegalArgumentException("Global must not be null");
            }
        }

    }

}

and

public final class ConcreteRequest extends AbstractRequest {

    private final String localValue;

    public ConcreteRequest(BuilderImpl builder) {
        super(builder);
        this.localValue = builder.localValue;
    }

    public static Builder builder() {
        return new BuilderImpl();
    }

    public String getLocalValue() {
        return localValue;
    }

    public interface Builder extends AbstractRequest.Builder<Builder, ConcreteRequest> {

        Builder localValue(String localValue);

    }

    public static final class BuilderImpl extends AbstractRequest.BuilderImpl<Builder, ConcreteRequest> implements Builder {

        private String localValue;

        @Override
        public Builder localValue(String localValue) {
            this.localValue = localValue;
            return this;
        }

        @Override
        public ConcreteRequest build() {
            this.validate();
            return new ConcreteRequest(this);
        }

        @Override
        public void validate() {
            super.validate();
            if (localValue == null) {
                throw new IllegalArgumentException("Local must not be null");
            }
        }

    }

}

And now I can see all methods:

enter image description here

jnemecz
  • 3,171
  • 8
  • 41
  • 77