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

Related searches

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?

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

[PDF] Declaring and Checking Non-null Types in an Object-Oriented , Not only are they hard to identify, but they're also complex to deal with. Learn about the Null Object Pattern and how to implement it in Java Thus, accessing any field, method, or index of a null object causes a parameters that aren't allowed to be null, it's better to check for every non-null parameter as� This causes a NullPointerException at line 6. Thus, accessing any field, method, or index of a null object causes a NullPointerException, as can be seen from the examples above. A common way of avoiding the NullPointerException is to check for null: public void doSomething() {. String result = doSomethingElse ();

Change your unsetAll method to setAll:

private void setAll(BigInteger numericParam, String stringParam, LocalDateTime dateParam) {
    this.numericParam = numericParam;
    this.stringParam = stringParam;
    this.dateParam = dateParam;
}

Then invoke from your public setters like:

public void setNumericParam(BigInteger numericParam) {
    setAll(Objects.requireNonNull(numericParam), null, null);
}

Note that Objects.requireNonNull is evaluated before setAll, so if you were to pass in a null numericParam, this would fail without changing any internal state.

Avoid Check for Null Statement in Java, Ignoring null fields or attribute is a one of the common requirement while NON_NULL) to only include non-null fields, thus excluding any attribute whose value is Java object with null fields using Jackson to understand the problem better. From a code point of view, restricting string functions to work only with one type, i.e. string (not null), makes them easier to prove; avoiding null is also a reason for having Option object. So, if the code that produces request/response doesn't use null, my guess is the code on the other side of the API won't be forced to use null too.

3 ways to ignore null fields while converting Java object to JSON , Returns the given object is it is non-null, otherwise returns the Supplier's Supplier .get() value. Find the "best guess" middle value among comparables. all values in the array are null s, false if there is at least one non-null value in the array. This method is similar to clone(Object) , but will return the provided instance as� Null (or NULL) is a special marker used in Structured Query Language to indicate that a data value does not exist in the database. Introduced by the creator of the relational database model, E. F. Codd, SQL Null serves to fulfil the requirement that all true relational database management systems (RDBMS) support a representation of "missing information and inapplicable information".

Why not simply?

public void setNumericParam(BigInteger numericParam) { 
     this.numericParam = Objects.requireNonNull(numericParam); 
     this.stringParam = null; 
     this.dateParam = null; 
}

ObjectUtils (Apache Commons Lang 3.11 API), That way also read-only properties can be constrained which have no setter method. It is recommended to stick either to field or property annotations within one class. not only allow to validate single class instances but also complete object graphs Note that null values are getting ignored during cascaded validation. void g_clear_weak_pointer ( gpointer *weak_pointer_location ); Clears a weak reference to a GObject. weak_pointer_location must not be NULL. If the weak reference is NULL then this function does nothing. Otherwise, the weak reference to the object is removed for that location and the pointer is set to NULL.

Your goal

You mention in the comments that your goal is to write SQL requests for a legacy DB:

type:VARCHAR, numeric: INT, string: VARCHAR, date: DATETIME and ExclusiveField will be used as getQueryRunner().query("CALL sp_insert_parametter(?, ?, ?, ?, ?)", param.getNumericParam(), id, type, param.getStringParam(), param.getDateParam())

So your goal really isn't to create a class with only one non-null field.

Alternative

You could define an abstract class Field with id, type, value attributes:

public abstract class Field
{
    private int id;
    private Class<?> type;
    private Object value;

    public Field(int id, Object value) {
        this.id = id;
        this.type = value.getClass();
        this.value = value;
    }

    public abstract int getPosition();
}

For each column in your database, you create a small corresponding class, extending Field. Each class defines its desired type and its position in the SQL command:

import java.math.BigInteger;


public class BigIntegerField extends Field
{
    public BigIntegerField(int id, BigInteger numericParam) {
        super(id, numericParam);
    }

    @Override
    public int getPosition() {
        return 0;
    }
}

You can define Field#toSQL:

public String toSQL(int columnsCount) {
    List<String> rows = new ArrayList<>(Collections.nCopies(columnsCount, "NULL"));
    rows.set(getPosition(), String.valueOf(value));
    return String.format("SOME SQL COMMAND (%d, %s, %s)", id, type.getName(), String.join(", ", rows));
}

Which will output NULLS everywhere except at the desired position.

That's it.

Complete code

Field.java

package com.stackoverflow.legacy_field;

import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;


public abstract class Field
{
    private int id;
    private Class<?> type;
    private Object value;

    public Field(int id, Object value) {
        this.id = id;
        this.type = value.getClass();
        this.value = value;
    }

    public abstract int getPosition();

    public static void main(String[] args) {
        List<Field> fields = Arrays.asList(new BigIntegerField(3, BigInteger.TEN),
                new StringField(17, "FooBar"),
                new DateTimeField(21, LocalDateTime.now()));
        for (Field field : fields) {
            System.out.println(field.toSQL(3));
        }
    }

    public String toSQL(int columnsCount) {
        List<String> rows = new ArrayList<>(Collections.nCopies(columnsCount, "NULL"));
        rows.set(getPosition(), String.valueOf(value));
        return String.format("SOME SQL COMMAND (%d, %s, %s)", id, type.getName(), String.join(", ", rows));
    }
}

BigIntegerField.java

package com.stackoverflow.legacy_field;

import java.math.BigInteger;


public class BigIntegerField extends Field
{
    public BigIntegerField(int id, BigInteger numericParam) {
        super(id, numericParam);
    }

    @Override
    public int getPosition() {
        return 0;
    }
}

StringField.java

package com.stackoverflow.legacy_field;

public class StringField extends Field
{
    public StringField(int id, String stringParam) {
        super(id, stringParam);
    }

    @Override
    public int getPosition() {
        return 1;
    }
}

DateTimeField.java

package com.stackoverflow.legacy_field;

import java.time.LocalDateTime;

public class DateTimeField extends Field
{

    public DateTimeField(int id, LocalDateTime value) {
        super(id, value);
    }

    @Override
    public int getPosition() {
        return 2;
    }
}
Result

Launching Field#main outputs:

SOME SQL COMMAND (3, java.math.BigInteger, 10, NULL, NULL)
SOME SQL COMMAND (17, java.lang.String, NULL, FooBar, NULL)
SOME SQL COMMAND (21, java.time.LocalDateTime, NULL, NULL, 2019-05-09T09:39:56.062)

Which should be really close to your desired output. You could probably find better names and define specific toString() methods if needed.

Chapter 2. Declaring and validating bean constraints, If you have an employee table that has a termination date field. A null value in the termination date is because it is a future required field which is currently unknown. Every employee be it active or terminated will at some point have a date added to that field. That is in my opinion the one and only reason for a Nullable field.

Schemas 2 and 3 would not. Schema 4 would only allow null if the referenced Fork schema is also nullable. Taking schema 2 as an example: Start with nullable: true and apply the interpretation nullable: true--> type: [t, 'null']. In this case, there is no type assertion, so t is the list of non-null types:

Make sure that fields can not hold null, unless specifically designed to do so. In my design, only the manager field can be null, and it is exposed to the user using the Optional class. Validate constructor and method parameters. Throw exceptions to prevent null references from leaking deeper into your class. Don't return null from methods.

These two are the result field and the returns(Object…) method: With the result field, you can define one return value for any non-void returning mocked method. This return value can also be an exception to be thrown (this time working for both non-void and void returning methods).

Comments
  • 1. if you pass in a null value to any of those, all fields will be left as null, which is probably not intentional 2. do you need the object to be mutable? Providing 3 different constructors (or factory methods if you prefer) would make this quite a bit cleaner and guarantee that every ExclusiveField object that's accessible is always valid.
  • @HieuHT but even so, it is better not to change the state when an exception occurs. This idea is called failure atomicity.
  • This feels like a broken requirement. I'm very very curious about what this class actually does ... I would almost never expect a class to behave like this. What are you ultimately trying to accomplish?
  • Beware that this is a common anti-pattern for cases more appropriately dealt with via subtyping--you are typing values by which pointer they would use, which is to say, choosing a pointer per which type a value is.
  • My point is that you are conceptually partitioning non-null values into disjoint categories/subtypes/subclasses. I am not saying that such a partitioning criterion is necessarily per language primitive subtypes or per some subclassing that you already have. I am saying it may be a criterion that should be embodied by subclasses/inheritance in your code. The question is are you better served by a single pointer that will be of one of multiple subclasses, instead of your current design of a radio button for each category of object.
  • that's the simplest approach? :|
  • @Eugene well, yes. 1) It’s the logical conclusion from "I want to have at most one value" to "I have only one field that can hold a value" 2) it is technically impossible that the constraint is ever violated 3) each property method is a simple single liner, simpler than any of the shown alternatives. 4) when the OP says, the number of fields could become huge, this is the most memory efficient solution.
  • @Eugene and don’t forget, I’ve shown the complete class, unlike the other answers showing only a fragment of the actual solution. Actually, in my solution, the bookkeeping is less than in the other approaches. Just consider how much code has to be touched to add another property and how the solution looks like for ten, twenty, or thirty fields.
  • +1 I agree that a mutually-exclusive variable should be held in a single field. I probably would have used fewer methods to access it, using a generic type parameter.
  • @JohnWu that’s what the (private in my example) get and set methods already do; the other methods are just for convenience; you could omit them if you prefer the generic methods as the API.
  • @dan1st your edit was not correct. OP is asking to enforce that at most (or perhaps exactly) one field is non-null. Your check would have ensured that at least one field is non-null.