在Java中检查两个参数,要么不为null,要么都为null

我使用spring引导来开发一个用于发送电子邮件的shell项目,例如

sendmail -from foo@bar.com -password foobar -subject "hello world" -to aaa@bbb.com 

如果缺lessfrom passwordpassword参数,我使用默认的发件人和密码,例如noreply@bar.com123456

所以如果用户传递了参数,他们也必须传递password参数,反之亦然。 也就是说,两者都是非空的,或者都是空的。

我如何优雅地检查这个?

现在我的方法是

 if ((from != null && password == null) || (from == null && password != null)) { throw new RuntimeException("from and password either both exist or both not exist"); } 

有一种使用^ ( XOR )运算符的方法:

 if (from == null ^ password == null) { // Use RuntimeException if you need to throw new IllegalArgumentException("message"); } 

如果只有一个variables为空, if条件成立。

但我认为通常情况下最好使用两个条件, if不同的exception消息。 你不能用单一的条件来定义什么地方出了问题。

 if ((from == null) && (password != null)) { throw new IllegalArgumentException("If from is null, password must be null"); } if ((from != null) && (password == null)) { throw new IllegalArgumentException("If from is not null, password must not be null"); } 

它更可读,更容易理解,只需要一点点额外的打字。

那么,这听起来像你试图检查两者的“无效”条件是否相同。 你可以使用:

 if ((from == null) != (password == null)) { ... } 

或者用辅助variables使其更加明确:

 boolean gotFrom = from != null; boolean gotPassword = password != null; if (gotFrom != gotPassword) { ... } 

就个人而言,我更喜欢可读性优雅。

 if (from != null && password == null) { throw new RuntimeException("-from given without -password"); } if (from == null && password != null) { throw new RuntimeException("-password given without -from"); } 

把这个function放在一个带有签名的2参数方法中:

 void assertBothNullOrBothNotNull(Object a, Object b) throws RuntimeException 

这节省了您感兴趣的实际方法中的空间,并使其更具可读性。 略有冗长的方法名没有什么错,而且很短的方法也没有错。

一个Java 8解决scheme将使用Objects.isNull(Object) ,假设一个静态导入:

 if (isNull(from) != isNull(password)) { throw ...; } 

对于Java <8(或者如果您不喜欢使用Objects.isNull() ),您可以轻松编写自己的isNull()方法。

这是任何数量的空检查的一般解决scheme

 public static int nulls(Object... objs) { int n = 0; for(Object obj : objs) if(obj == null) n++; return n; } public static void main (String[] args) throws java.lang.Exception { String a = null; String b = ""; String c = "Test"; System.out.println (" "+nulls(a,b,c)); } 

用途

 // equivalent to (a==null & !(b==null|c==null) | .. | c==null & !(a==null|b==null)) if (nulls(a,b,c) == 1) { .. } // equivalent to (a==null | b==null | c==null) if (nulls(a,b,c) >= 1) { .. } // equivalent to (a!=null | b!=null | c!=null) if (nulls(a,b,c) < 3) { .. } // equivalent to (a==null & b==null & c==null) if (nulls(a,b,c) == 3) { .. } // equivalent to (a!=null & b!=null & c!=null) if (nulls(a,b,c) == 0) { .. } 

既然您想在发件人和密码都不存在时做一些特殊的事情(使用默认设置),请先处理。
之后,你应该有一个发件人和密码发送电子邮件; 抛出一个exception,如果任何一个丢失。

 // use defaults if neither is provided if ((from == null) && (password == null)) { from = DEFAULT_SENDER; password = DEFAULT_PASSWORD; } // we should have a sender and a password now if (from == null) { throw new MissingSenderException(); } if (password == null) { throw new MissingPasswordException(); } 

一个额外的好处是,如果你的默认值为null,那么也会被检测到。


话虽如此, 一般而言,我认为当您需要的运营商使用XOR时应该是允许的。 它语言的一部分,不只是因为一个神秘的编译器bug而产生的一些技巧。
我曾经有一个牛的工作者发现三元操作员太混乱了…

我想build议另一个替代scheme,我将如何写这段代码:

 if( from != null ) { if( password == null ) error( "password required for " + from ); } else { if( password != null ) warn( "the given password will not be used" ); } 

对我而言,这似乎是expression这种情况的最自然的方式,这使得将来某些人可能需要阅读这些情况才容易理解。 它还允许您提供更有用的诊断消息,并将不必要的密码视为不那么严重,并且可以很容易地修改这种情况。 也就是说,你可能会发现给一个口令作为命令行参数并不是最好的想法,并且可能希望允许从标准input中读取密码(如果缺less参数)。 或者你可能想默默地忽略多余的密码参数。 像这样的变化不会要求你重写整个事情。

除此之外,它只执行最less的比较次数,所以并不比更“优雅”的select更昂贵。 虽然性能是不太可能的问题,因为开始一个新的进程已经比一个额外的空检查贵得多。

我认为处理这个问题的一个正确的方法是考虑三种情况:提供'from'和'password',既不提供,也提供两者的组合。

 if(from != null && password != null){ //use the provided values } else if(from == null && password == null){ //both values are null use the default values } else{ //throw an exception because the input is not correct. } 

这听起来像原来的问题想要打破stream动,如果它是不正确的input,但他们将不得不稍后重复一些逻辑。 也许一个好的投掷陈述可能是:

 throw new IllegalArgumentException("form of " + form + " cannot be used with a " + (password==null?"null":"not null") + " password. Either provide a value for both, or no value for both" ); 

这是一个相对直接的方式,不涉及任何异或冗长的ifs。 但是,它确实需要稍微冗长一些,但是在优势方面,您可以使用我build议的自定义exception来获取更有意义的错误消息。

 private void validatePasswordExists(Parameters params) { if (!params.hasKey("password")){ throw new PasswordMissingException("Password missing"); } } private void validateFromExists(Parameters params) { if (!params.hasKey("from")){ throw new FromEmailMissingException("From-email missing"); } } private void validateParams(Parameters params) { if (params.hasKey("from") || params.hasKey("password")){ validateFromExists(params); validatePasswordExists(params); } } 

似乎没有人提到三元运算符 :

 if (a==null? b!=null:b==null) 

很好地检查这个特定的条件,但不能很好地概括两个variables。

正如我看到你的意图,没有必要总是检查两个独占无效,但检查password是否为空当且仅当from不是空。 如果from为null from则可以忽略给定的password参数并使用您自己的默认值。

写在伪必须是这样的:

 if (from == null) { // form is null, ignore given password here // use your own defaults } else if (password == null) { // form is given but password is not // throw exception } else { // both arguments are given // use given arguments } 

我很惊讶没有人提到frompassword字段的一个简单的解决scheme,并通过引用该类的一个实例:

 class Account { final String name, password; Account(String name, String password) { Objects.requireNonNull(name, () -> "name"); Objects.requireNonNull(password, () -> "password"); this.name = name; this.password = password; } } // the code that requires an account Account from; // do stuff 

这里from可以是null或非null,如果它是非null,它的两个字段都有非null值。

这种方法的一个优点是,一个字段的错误,而不是另一个字段的空值会在最初获取账户的时候被触发,而不是在使用账户的代码运行的时候。 在使用账户的代码被执行的时候,数据不可能是无效的。

这种方法的另一个优点是可读性更高,因为它提供了更多的语义信息。 另外,很可能您需要在其他地方同时使用名称和密码,因此定义一个附加类的成本可以在多种用途上分摊。