Search code examples
javagenericsinheritancelombokbuilder-pattern

Inherit variables from abstract superclass


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.


Solution

  • @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.