Lombok Inherithance: how to set default value for field in superclass without redeclaring it?

Viewed 548

I am using Lombok and I need a way to automatically set the value for a superclass field in the subclass, without redeclaring it.

It is working fine on its own (@SuperBuiler and @Builder.Default) when using the builder, but it is giving troubles with Spring-Data MongoDB.

org.springframework.data.mapping.MappingException: Ambiguous field mapping detected! Both protected test.jackson.polymorphism.domain.enumeration.EvaluationType test.jackson.polymorphism.domain.models.EvaluationModel.type and private test.jackson.polymorphism.domain.enumeration.EvaluationType test.jackson.polymorphism.domain.models.QuantitativeEvaluationModel.type map to the same field name type! Disambiguate using @Field annotation!

    at org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity$AssertFieldNameUniquenessHandler.assertUniqueness(BasicMongoPersistentEntity.java:368)
    at org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity$AssertFieldNameUniquenessHandler.doWithPersistentProperty(BasicMongoPersistentEntity.java:354)
    at org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity$AssertFieldNameUniquenessHandler.doWithPersistentProperty(BasicMongoPersistentEntity.java:348)

These are my classes:

public enum EvaluationType{
    QUALITATIVE, QUANTITATIVE;
}

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public abstract class EvaluationModel {
    protected EvaluationType type;
    protected Integer evaluation;
}

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class QualitativeEvaluationModel extends EvaluationModel {
    /**
     * How to set the default value for this field
     * without redeclaring it?
     */
    @Builder.Default
    private EvaluationType type = EvaluationType.QUALITATIVE;
}

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class QuantitativeEvaluationModel extends EvaluationModel {
    /**
     * How to set the default value for this field
     * without redeclaring it?
     */
    @Builder.Default
    private EvaluationType type = EvaluationType.QUANTITATIVE;

    @Builder.Default
    private String currencyCode = Currency.getInstance("EUR").getCurrencyCode();

    private BigDecimal economicValue;
}

How do I make these subclass to have a default value (mayb final) for the type field, but without redeclaring the field? This should work both with the contructor and with the builder.

2 Answers

From your description, it sounds a bit like that you don't want a settable field, but just a fixed value depending on the subclass instance. In that case, a manually implemented getter method without any field is the better choice.

However, if you really want the user to be able to set a value different from the default, this is your way:

@SuperBuilder
public class QualitativeEvaluationModel extends EvaluationModel {
    
    public static abstract class QualitativeEvaluationModelBuilder<C extends QualitativeEvaluationModel, B extends QualitativeEvaluationModelBuilder<C, B>> extends EvaluationModel.EvaluationModelBuilder<C, B>
    {
        public QualitativeEvaluationModelBuilder() {
            type(EvaluationType.QUALITATIVE);
        }
    }
}

It defines the builder class exactly like Lombok would create it (Lombok will add anything that you didn't write manually). As it's really filled with generics, I used delombok to get it right. Next, you have to define a default value for your builder instance simply by calling the builder's setter method for type in the builder's constructor.

In this way, users that don't call the type() method on the builder instance will get the default value. If you want something else as type, just call the type() method on the builder instance.

Note that this only affects the builder. If you also want to instantiate via (no-args) constructor, you have to manually implement the constructor and set the default there.

Related