Проверьте два аргумента в Java, либо не нулевые, либо оба нулевые элегантно

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

sendmail -from [email protected] -password  foobar -subject "hello world"  -to [email protected]

Если аргументы from и password отсутствуют, я использую отправителя и пароль по умолчанию, например. [email protected] и 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");
}

Ответ 1

Существует способ использования оператора ^ (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");
}

Это более читаемо и намного легче понять, и это требует лишь немного дополнительного ввода.

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

void assertBothNullOrBothNotNull(Object a, Object b) throws RuntimeException

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

Ответ 10

Здесь относительно прямолинейный способ, который не связан с Xor og longy ifs. Однако он требует, чтобы вы были немного более подробными, но, с другой стороны, вы можете использовать пользовательские исключения, которые я предложил получить более содержательное сообщение об ошибке.

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

Ответ 11

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

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

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

Ответ 12

Как я вижу ваши намерения, нет необходимости всегда проверять оба исключительных значения, но проверять, является ли 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
}

Ответ 13

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

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 может быть нулевым или непустым, а если он не равен нулю, то оба его поля имеют ненулевые значения.

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

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