繁体   English   中英

如何检查多个对象中只有一个是非空的?

[英]How to check that only one object of many is non-null?

三个对象中只有一个不能为空,另外两个必须为空。 显而易见的幼稚解决方案是这样的:

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

是否有更优雅的解决方案来确保只有一个对象不为空?

好吧,我可能会重新考虑拥有所有不同参数的设计,其中只有一个参数必须是非空的。

但无论如何,你需要采取两个行动:

  • 检查一个参数是否为非空
  • 然后调用基于非空参数的方法。

可以通过首先编写一个返回参数列表的非空索引位置的方法来实现这一点,但前提是这是唯一的非空值:

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

然后您可以创建一个Consumer列表,每个都调用自己的方法:

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

最后,我完全同意安迪·特纳的观点:

如果只是三件事,我会坚持你所拥有的。

老实说,我认为你所拥有的一切都很好。 看起来有点乱,而且容易出错(参数号错误,把!===搞混了); 但几乎你所做的任何事情都会让人一目了然。

例如,您可以引入这样的方法:

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

(您可以在“必须为空”参数上使其可变,但这在这里是不必要的,因为您总是在做 3 件事)

并像调用

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

这解决了混淆!===的问题; 但是混淆参数数字仍然很容易; 而且方法不标准,所以你不一定一眼就能理解它的含义。

您可以创建一个数组并简单地计算循环中的空值。 如果元素不止三个,这应该很有效,并且我们正在检查它们之间是否恰好有一个null:

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;

在回答时,问题的标题或文本中没有任何地方写到需要知道哪个元素为空。

您可以执行将恰好一个非空参数作为前提条件的强制要求。

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

您可以使用可变参数创建方法并计算非空对象。

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

然后你可以像这样使用你的 if 语句

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

注意:您可以使用任意数量的对象

我认为你应该坚持你所拥有的。 但是,如果您想混淆您的同事或您的特征自我,那么下面的内容应该是等效的:

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

为了添加另一个选项:

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 */;
}

前 3 个 if 非常相似。 并且该方法可以很容易地用于处理参数数组(将 action 设置为i + 1 ,其中i是用于遍历数组的变量)。

如果条件彼此不同,则您内部的方法将无法降低代码的复杂性。 然而你可以让你的代码更干净,如下所示,

注意:这里我假设您的paramsString类型

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

或使用 Enum 调用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();
}

我更喜欢使用 Stream 来检查这些参数。

if (Stream.of(param1, param2, param3).filter(Objects::nonNull).count() != 1) {
    throw new Exception();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM