Я использовал spring boot для разработки проекта оболочки, используемого для отправки электронной почты, например

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

Если аргументы from и password отсутствуют, я использую отправителя и пароль по умолчанию, например noreply@bar.com и 123456.

Таким образом, если пользователь передает аргумент from, он также должен передать аргумент password и наоборот. То есть либо оба не равны нулю, либо оба равны нулю.

Как мне элегантно это проверить?

Теперь мой путь

if ((from != null && password == null) || (from == null && password != null)) {
    throw new RuntimeException("from and password either both exist or both not exist");
}
164
zhuguowei 4 Янв 2016 в 09:57

13 ответов

Лучший ответ

Есть способ использовать оператор ^ (XOR):

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

Условие if будет истинным, если только одна переменная имеет значение NULL.

Но я думаю, что обычно лучше использовать два условия if с разными сообщениями об исключениях. Вы не можете определить, что пошло не так, используя одно условие.

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

Он более читабелен и понятен, требует лишь небольшого дополнительного набора текста.

334
coolguy 10 Апр 2016 в 11:45

Как я понимаю ваши намерения, нет необходимости всегда проверять оба исключительных значения NULL, но нужно проверять, является ли password нулевым тогда и только тогда, когда from не равно нулю. Вы можете игнорировать данный аргумент password и использовать собственное значение по умолчанию, если from имеет значение null.

Написано псевдо должно быть так:

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
}
5
JordiVilaplana 4 Янв 2016 в 08:46

Вот общее решение для любого количества нулевых проверок

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) { .. }
9
Khaled.K 9 Янв 2016 в 10:29

Похоже, никто не упомянул тернарный оператор:

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

Хорошо работает для проверки этого конкретного условия, но плохо обобщает две переменные.

6
gbronner 8 Янв 2016 в 17:03

Поскольку вы хотите сделать что-то особенное (использовать значения по умолчанию), когда отправитель и пароль отсутствуют, обработайте это в первую очередь.
После этого у вас должны быть и отправитель, и пароль для отправки электронного письма; генерировать исключение, если что-либо отсутствует.

// 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 должно быть допустимо, когда это тот оператор, который вам нужен. Это часть языка, а не какой-то трюк, работающий из-за загадочной ошибки компилятора.
Однажды у меня был коровник, который нашел тернарный оператор слишком запутанным для использования ...

9
SQB 15 Янв 2016 в 09:48

Я удивлен, что никто не упомянул простое решение создания полей from и password класса и передачи ссылки на экземпляр этого класса:

class Account {
    final String name, password;
    Account(String name, String password) {
        this.name = Objects.requireNonNull(name, "name");
        this.password = Objects.requireNonNull(password, "password");
    }
}

// the code that requires an account
Account from;
// do stuff

Здесь from может иметь значение NULL или ненулевое значение, и если оно не равно NULL, оба его поля имеют ненулевые значения.

Одним из преимуществ этого подхода является то, что ошибка создания одного поля, но не другого поля, становится нулевым, срабатывает там, где изначально получена учетная запись, а не при запуске кода, использующего учетную запись. К моменту выполнения кода, использующего учетную запись, данные не могут быть недействительными.

Еще одно преимущество этого подхода - более читабельный, поскольку он предоставляет больше семантической информации. Кроме того, вполне вероятно, что вам потребуется вместе имя и пароль в других местах, поэтому стоимость определения дополнительного класса амортизируется за несколько использований.

4
Community 15 Сен 2018 в 05:57

Я думаю, что правильный способ справиться с этим - рассмотреть три ситуации: предоставлены и «от», и «пароль», ни один из них не предоставляется, предоставляется сочетание двух.

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

Похоже, что исходный вопрос хочет прервать поток, если это неправильный ввод, но потом им придется повторить часть логики позже. Возможно, хорошее заявление о броске может быть таким:

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"
);
7
matt 5 Янв 2016 в 19:45

Я хотел бы предложить другую альтернативу, как я бы написал этот фрагмент кода:

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

Мне кажется, что это самый естественный способ выразить это состояние, которое упрощает понимание для тех, кому, возможно, придется прочитать это в будущем. Это также позволяет вам выдавать более полезные диагностические сообщения и относиться к ненужному паролю как к менее серьезному, а также упрощает изменение, что весьма вероятно при таком состоянии. Т.е. вы можете обнаружить, что использование пароля в качестве аргумента командной строки - не лучшая идея, и вы можете захотеть разрешить чтение пароля со стандартного ввода, если аргумент отсутствует. Или вы можете игнорировать ненужный аргумент пароля. Подобные изменения не потребуют от вас переписывания всего этого.

Кроме того, он выполняет только минимальное количество сравнений, поэтому не дороже, чем более "элегантные" альтернативы. Хотя производительность здесь маловероятна, потому что запуск нового процесса уже намного дороже, чем дополнительная нулевая проверка.

8
x4u 5 Янв 2016 в 23:02

Решением Java 8 будет использование { {X0}} при статическом импорте:

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

Для Java <8 (или если вам не нравится использовать Objects.isNull()), вы можете легко написать свой собственный метод isNull().

11
Didier L 6 Янв 2016 в 10:05

Лично я предпочитаю удобочитаемость элегантному.

if (from != null && password == null) {
    throw new RuntimeException("-from given without -password");
}
if (from == null && password != null) {
    throw new RuntimeException("-password given without -from");
}
222
jpmc26 5 Янв 2016 в 16:45

Что ж, похоже, вы пытаетесь проверить, совпадает ли условие "недействительности" этих двух элементов или нет. Вы можете использовать:

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

Или сделайте это более явным с помощью вспомогательных переменных:

boolean gotFrom = from != null;
boolean gotPassword = password != null;
if (gotFrom != gotPassword)
{
    ...
}
287
Jon Skeet 4 Янв 2016 в 06:59

Поместите эту функциональность в метод с двумя аргументами с подписью:

void assertBothNullOrBothNotNull(Object a, Object b) throws RuntimeException

Это экономит место в интересующем вас методе и делает его более читабельным. Нет ничего плохого в слегка подробных именах методов и нет ничего плохого в очень коротких методах.

16
Traubenfuchs 4 Янв 2016 в 09:34

Вот относительно простой способ, не требующий каких-либо длительных операций Xor и длинных if. Однако это требует, чтобы вы были немного более подробными, но с другой стороны, вы можете использовать настраиваемые исключения, которые я предложил, чтобы получить более значимое сообщение об ошибке.

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);
  }
}
6
Arnab Datta 6 Янв 2016 в 13:03