Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
87 views
in Technique[技术] by (71.8m points)

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

I want to write a class with more than 1 fields of different types but at any time, there is one and only one field of an instance object having non null value.

What I did so far does not look really clean.

class ExclusiveField {

    private BigInteger numericParam;
    private String stringParam;
    private LocalDateTime dateParam;

    public void setNumericParam(BigInteger numericParam) {
        unsetAll();
        this.numericParam = Objects.requireNonNull(numericParam);
    }

    public void setStringParam(String stringParam) {
        unsetAll();
        this.stringParam = Objects.requireNonNull(stringParam);
    }

    public void setDateParam(LocalDateTime dateParam) {
        unsetAll();
        this.dateParam = Objects.requireNonNull(dateParam);
    }

    private void unsetAll() {
        this.numericParam = null;
        this.stringParam = null;
        this.dateParam = null;
    }
}

Does Java support this pattern somehow or is there a more decent way to do it?

question from:https://stackoverflow.com/questions/56037079/what-is-a-good-way-to-allow-only-one-non-null-field-in-an-object

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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);
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...