What is a good way to allow only one non null field in an object

The simplest approach for an object to have only one non-null field, is to actually have only one field and assume all others to be null implicitly. You only need another tag field, to determine which field is non-null.

Since in your example, all alternatives seem to be about the type of the value, the type itself could be the tag value, e.g.

class ExclusiveField {
    private Class<?> type;
    private Object value;

    private <T> void set(Class<T> t, T v) {
        value = Objects.requireNonNull(v);
        type = t;
    }
    private <T> T get(Class<T> t) {
        return type == t? t.cast(value): null;
    }

    public void setNumericParam(BigInteger numericParam) {
        set(BigInteger.class, numericParam);
    }

    public BigInteger getNumericParam() {
        return get(BigInteger.class);
    }

    public void setStringParam(String stringParam) {
        set(String.class, stringParam);
    }

    public String getStringParam() {
        return get(String.class);
    }

    public void setDateParam(LocalDateTime dateParam) {
        set(LocalDateTime.class, dateParam);
    }

    public LocalDateTime getDateParam() {
        return get(LocalDateTime.class);
    }
}

If the type is not the only differentiator, you need to define distinct key values. An enum would be a natural choice, but unfortunately, enum constants can not provide the type safety. So, the alternative would look like:

class ExclusiveField {
    private static final class Key<T> {
        static final Key<String>        STRING_PROPERTY_1 = new Key<>();
        static final Key<String>        STRING_PROPERTY_2 = new Key<>();
        static final Key<BigInteger>    BIGINT_PROPERTY   = new Key<>();
        static final Key<LocalDateTime> DATE_PROPERTY     = new Key<>();
    }
    private Key<?> type;
    private Object value;

    private <T> void set(Key<T> t, T v) {
        value = Objects.requireNonNull(v);
        type = t;
    }

    @SuppressWarnings("unchecked") // works if only set() and get() are used
    private <T> T get(Key<T> t) {
        return type == t? (T)value: null;
    }

    public void setNumericParam(BigInteger numericParam) {
        set(Key.BIGINT_PROPERTY, numericParam);
    }

    public BigInteger getNumericParam() {
        return get(Key.BIGINT_PROPERTY);
    }

    public void setString1Param(String stringParam) {
        set(Key.STRING_PROPERTY_1, stringParam);
    }

    public String getString1Param() {
        return get(Key.STRING_PROPERTY_1);
    }

    public void setString2Param(String stringParam) {
        set(Key.STRING_PROPERTY_2, stringParam);
    }

    public String getString2Param() {
        return get(Key.STRING_PROPERTY_2);
    }

    public void setDateParam(LocalDateTime dateParam) {
        set(Key.DATE_PROPERTY, dateParam);
    }

    public LocalDateTime getDateParam() {
        return get(Key.DATE_PROPERTY);
    }
}

Leave a Comment