在Java中检查两个参数,要么不为null,要么都为null
我使用spring引导来开发一个用于发送电子邮件的shell项目,例如
sendmail -from foo@bar.com -password foobar -subject "hello world" -to aaa@bbb.com
如果缺lessfrom
password
和password
参数,我使用默认的发件人和密码,例如noreply@bar.com
和123456
。
所以如果用户传递了参数,他们也必须传递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 }
我很惊讶没有人提到from
和password
字段的一个简单的解决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值。
这种方法的一个优点是,一个字段的错误,而不是另一个字段的空值会在最初获取账户的时候被触发,而不是在使用账户的代码运行的时候。 在使用账户的代码被执行的时候,数据不可能是无效的。
这种方法的另一个优点是可读性更高,因为它提供了更多的语义信息。 另外,很可能您需要在其他地方同时使用名称和密码,因此定义一个附加类的成本可以在多种用途上分摊。