Inherit variables from abstract superclass

130 Views Asked by At

I'm trying to implement a BuilderPattern, where a subclass must extend a superclass.

Superclass:

@Getter
public abstract class CommonValidatorConfig<VC extends CommonValidatorConfig<VC>> {

    private boolean canBeNull;
    private boolean canBeEmpty;
    
    public static abstract class CommonValidatorConfigBuilder<VC, VCB extends CommonValidatorConfigBuilder<VC, VCB>> {
        
        protected boolean canBeNull;
        protected boolean canBeEmpty;
        
        @SuppressWarnings("unchecked")
        public VCB canBeNull(boolean canBeNull) {
            this.canBeNull = canBeNull;
            return (VCB) this;
        }
        
        @SuppressWarnings("unchecked")
        public VCB canBeEmpty(boolean canBeEmpty) {
            this.canBeEmpty = canBeEmpty;
            return (VCB) this;
        }
        
        @SuppressWarnings("unchecked")
        public VCB setDefault() {
            this.canBeNull = false;
            this.canBeEmpty = false;
            return (VCB) this;
        }
        
        public abstract VC build();
        
    }
    
}

Subclass:

@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class StringValidatorConfig extends CommonValidatorConfig<StringValidatorConfig> {
    
    public static class StringValidatorConfigBuilder extends CommonValidatorConfigBuilder<StringValidatorConfig, StringValidatorConfigBuilder> {

        @Override
        public StringValidatorConfig build() {
            return new StringValidatorConfig(false, false); // ERROR
        }
        
    }
    
}

The AllArgsConstructor AccessLevel is set to PRIVATE because I want to create a new instance using only Builders.

I was expecting an AllArgsConstructor for StringValidatorConfig with two variables (canBeNull and canBeEmpty), but the AllArgsConstructor takes no arguments for the constructor.

this means that the variables of the CommonValidatorConfig are not inherited.

Any help, also tutorials/docs/references or improvements of code are welcomed.

2

There are 2 best solutions below

0
Jan Rieke On BEST ANSWER

@SuperBuilder will do all the work for you:

@Getter
@SuperBuilder
public abstract class CommonValidatorConfig {
    private boolean canBeNull;
    private boolean canBeEmpty;
}

@SuperBuilder
public class StringValidatorConfig extends CommonValidatorConfig {
}

That's it.

If you need to add a custom method inside your builder, you can do so by adding the class header of the (abstract) builder class and add your method. Lombok will add all the rest of its methods. I suggest you copy the class header from the delombok output (run java -jar path/to/lombok.jar delombok -p path/to/ClassWithSuperBuilder.java).

@Getter
@SuperBuilder
public abstract class CommonValidatorConfig {
    private boolean canBeNull;
    private boolean canBeEmpty;

    public static abstract class CommonValidatorConfigBuilder<C extends CommonValidatorConfig, B extends CommonValidatorConfig.CommonValidatorConfigBuilder<C, B>> {
        public B setDefault() {
            this.canBeNull = false;
            this.canBeEmpty = false;
            return self();
        }
    }
}

The "experimental" status of @SuperBuilder just means it may not receive bugfixes as quickly as stable features. Furthermore, there are plans to promote @SuperBuilder to stable.

The code Lombok generates is completely type-safe, there are no unchecked type conversions. So even if you later decide that you don't want @SuperBuilder anymore, you can simply de-lombok it. The resulting code will be better than your manual solution.

2
Paul Marcelin Bejan On

I'm not sure if this is the best way, but I solved in this way:

Superclass:
I added an AllArgsConstructor with AccessLevel PROTECTED.

@Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class CommonValidatorConfig<VC extends CommonValidatorConfig<VC>> {

    private boolean canBeNull;
    private boolean canBeEmpty;
    
    public static abstract class CommonValidatorConfigBuilder<VC, VCB extends CommonValidatorConfigBuilder<VC, VCB>> {
        
        protected boolean canBeNull;
        protected boolean canBeEmpty;
        
        @SuppressWarnings("unchecked")
        public VCB canBeNull(boolean canBeNull) {
            this.canBeNull = canBeNull;
            return (VCB) this;
        }
        
        @SuppressWarnings("unchecked")
        public VCB canBeEmpty(boolean canBeEmpty) {
            this.canBeEmpty = canBeEmpty;
            return (VCB) this;
        }
        
        @SuppressWarnings("unchecked")
        public VCB setDefault() {
            this.canBeNull = false;
            this.canBeEmpty = false;
            return (VCB) this;
        }
        
        public abstract VC build();
        
    }
    
}

Subclass:
1 - Removed lombok AllArgsConstructor.
2 - Declared the AllArgsConstructor and passed the variables to superclass constructor.
3 - Access to superclass variables using super keyword.

@Builder
public class StringValidatorConfig extends CommonValidatorConfig<StringValidatorConfig> {
    
    private StringValidatorConfig(boolean canBeNull, boolean canBeEmpty) {
        super(canBeNull, canBeEmpty);
    }
    
    public static class StringValidatorConfigBuilder extends CommonValidatorConfigBuilder<StringValidatorConfig, StringValidatorConfigBuilder> {

        @Override
        public StringValidatorConfig build() {
            return new StringValidatorConfig(super.canBeNull, super.canBeEmpty);
        }
        
    }
    
}