Home > Enterprise >  How to check that only one object of many is non-null?
How to check that only one object of many is non-null?

Time:10-01

Only one object of three must not be null, two others must be null. The obvious naive solution would be like that:

    if (param1 != null && param2 == null && param3 == null) {
        doSomething(param1);
    } else if (param1 == null && param2 != null && param3 == null) {
        doSomethingElse(param2);
    } else if (param1 == null && param2 == null && param3 != null) {
        doOtherThings(param3);
    }

Is there more elegant solution to ensure that only one object is not null?

CodePudding user response:

Well, I would maybe rethink the design of having all different parameters of which just a single one must be nonnull.

But anyways, you need to take two actions:

  • check if exactly one param is nonnull
  • then invoke a method based on the parameter which is nonnull.

You could achieve this by first writing a method which returns the position of the nonnull index of a list of params, but only if that's the only nonnull value:

OptionalInt nonnullIndex(List<?> params) {
    var nonnulls = IntStream.range(0, params.size())
        .filter(i -> params.get(i) != null)
        .toArray();
    return (nonnulls.length == 1 ? OptionalInt.of(nonnulls[0]) : OptionalInt.empty());
}

And then you could create a list of Consumers which each invoke their own method:

List<Consumer<String>> consumers = List.of(
    s -> doSomething(s),
    s -> doSomethingElse(s),
    s -> doOtherThings(s)
);

List<String> params = Arrays.asList(param1, param2, param3);
int index = nonnullIndex(params)
    .orElseThrow(IllegalArgumentException::new);
consumers.get(index).accept(params.get(index));

At last, I totally agree with Andy Turner:

If it's just 3 things, I'd stick with what you've got.

CodePudding user response:

You can create an array and simply count nulls in a loop. This should work well if there are more than just three elements, and we are checking for exactly one null between them:

Object [] elements = { "a", null, "b" };
int s = 0;
for (int p = 0; p < elements.length; p  ) {
    if (elements[p] == null) {
        s  ;
    }
}
boolean oneNull = s == 1;

At the time of answering, nowhere in the header or text of the question is written about the need to know which element is null.

CodePudding user response:

Honestly, I think what you have is fine. It's a little bit messy to look at, and error-prone (getting the param number wrong, getting != and == mixed up); but almost anything you do will just make it harder to understand at a glance.

For example, you could introduce a method like this:

boolean onlyFirstNonNull(Object first, Object second, Object third) {
  return first != null && second == null && third == null;
}

(You can make it variadic on the "must be null" parameters, but that's unnecessary here since you're always doing 3 things)

and invoke like

    if (onlyFirstNonNull(param1, param2, param3)) {
        doSomething(param1);
    } else if (onlyFirstNonNull(param2, param1, param3)) {
        doSomethingElse(param2);
    } else if (onlyFirstNonNull(param3, param1, param2)) {
        doOtherThings(param3);
    }

This gets around the problem of mixing up != and ==; but it's still easy to mix up param numbers; and the method isn't standard, so you don't necessarily grok what it means at a glance.

CodePudding user response:

I think you should stick with what you have. But if you want to confuse your co-workers or your feature-self something like below should be eqivalent:

String res = "";
res  = param1 == null ? 0:1;
res  = param2 == null ? 0:1;
res  = param3 == null ? 0:1;
    
switch (res){
    case "100": doSomething(param1);     break;
    case "010": doSomethingElse(param2); break;
    case "001": doOtherThings(param3);   break;
}

CodePudding user response:

In order to add another option:

int action = 0;
if (param1 != null) {
    if (action == 0) action = 1;
    else  /* throw some error */;
}
if (param2 != null) {
    if (action == 0) action = 2;
    else  /* throw some error */;
}
if (param3 != null) {
    if (action == 0) action = 3;
    else  /* throw some error */;
}
switch (action) {
    case 1: soSomething(); break;
    case 2: doSomethingElse(); break;
    case 3: doOtherThings(); break;
    else:   /* throw error */;
}

The first 3 ifs are very similar. And the method can easily be used to process an array of arguments (set action to i 1, where i is the variable used to iterate through the array).

CodePudding user response:

You can create a method using varargs and count the non-null objects.

public static long getCountOfNonNullObjects(Object ... objects)
{
    return Arrays.stream(objects).filter(Objects::nonNull).count();
}

Then you can use your if statement like this

if (getCountOfNonNullObjects(obj,obj,obj) == 1)
    doSomething();

Note: You can use as many objects as you want

CodePudding user response:

The methods inside you if conditions are different from each other the complexity of your code cannot reduce. Yet you can make your code more clean like follows,

Note: Here I have assumed your params are type String

public boolean execute(String param , String... params) {
    return param != null && Arrays.stream(params).filter(Objects::isNull).count() == 2;
}
if      (execute(param1, param2, param3)) {doSomething(param1);}
else if (execute(param2, param1, param3)) {doSomethingElse(param2);}
else if (execute(param3, param1, param2)) {doOtherThings(param3);}

CodePudding user response:

You can perform the enforcing the requirement of having exactly one non-null param as a precondition.

boolean hasExactlyOneNonNullParam = List.of(param1, param2, param3)
  .stream()
  .filter(Objects::nonNull)
  .count() == 1;

if (!hasExactlyOneNonNullParam) {
  // handle failed precondition
  return;
}

if (param1 != null) {
    doSomething(param1);
} else if (param2 != null) {
    doSomethingElse(param2);
} else {
    doOtherThings(param3);
}

CodePudding user response:

Or with Enum call ParamEnum.doSomething(par1, par2, par3)


public enum ParamEnum {
    PARAM1 {
        @Override
        protected boolean accepted(String par1, String par2, String par3) {
            return par1 != null;
        }

        @Override
        protected void doOperation() {
            doSomethingImportant();
        }
    }, PARAM2 {
        @Override
        protected boolean accepted(String par1, String par2, String par3) {
            return par2 != null;
        }

        @Override
        protected void doOperation() {
            doSomethingElse();
        }
    }, PARAM3 {
        @Override
        protected boolean accepted(String par1, String par2, String par3) {
            return par3 != null;
        }

        @Override
        protected void doOperation() {
            doOtherThings();
        }
    };

    public static void doSomething(String par1, String par2, String par3) {
        List<ParamEnum> paramEnums =
                Arrays.stream(ParamEnum.values()).filter(e -> e.accepted(par1, par2, par3)).collect(Collectors.toList());
        if(paramEnums.size() == 1) {
            paramEnums.get(0).doOperation();
        }
    }

    abstract boolean accepted(String par1, String par2, String par3);
    abstract void doOperation();
}
  •  Tags:  
  • java
  • Related