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 Consumer
s 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();
}