Почему checkArgument предварительных условий Google Guava не возвращает значение?

Мне очень нравится, как библиотека guava позволяет использовать простые однострочники для проверки на null:

public void methodWithNullCheck(String couldBeNull) {
    String definitelyNotNull = checkNotNull(couldBeNull);
    //...
}

к сожалению, для простой проверки аргументов вам нужно как минимум две строки кода:

public void methodWithArgCheck(String couldBeEmpty) {
    checkArgument(!couldBeEmpty.isEmpty());
    String definitelyNotEmpty = couldBeEmpty;
    //...
}

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

public void methodWithEnhancedArgCheck(String couldBeEmpty) {
    String definitelyNotEmpty = EnhancedPreconditions.checkArgument(couldBeEmpty, !couldBeEmpty.isEmpty());
    //...
}

static class EnhancedPreconditions {
    public static <T> T checkArgument(T reference, boolean expression) {
        if (!expression) {
            throw new IllegalArgumentException();
        }

        return reference;
    }
}

Мне просто было интересно, так ли это по замыслу и стоит ли для этого запрашивать функцию.

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

public class SomeClassWithDependency {

    private final SomeDependency someDependency;

    public SomeClassWithDependency(SomeDependency someDependency) {
        this.someDependency = checkNotNull(someDependency);
    }

    //...

EDIT: принимаю ответ Низе, потому что я согласен с ним в отношении побочных эффектов и рассуждений о согласованности. Кроме того, если вы посмотрите на комментарий Xaerxess, похоже, что это вызывает путаницу и у других разработчиков.


person Petro Semeniuk    schedule 29.07.2012    source источник
comment
В дополнение к вашему примеру с checkArgument(T ref, boolean expr) недавно возникла проблема # 1038 обсуждение Preconditions.checkArgument(T ref, Predicate<T> test) было отклонено. Вы всегда можете создать класс Preconditions2 со своими методами.   -  person Xaerxess    schedule 30.07.2012


Ответы (3)


Чего я никогда не понимал, так это того, почему checkNotNull() вообще возвращает свой аргумент:

public void foo(String bar) {
    Preconditions.checkNotNull(bar);
    // here, you're sure that bar is not null. 
    // No need to use another variable or to reassign bar to the result 
    // of checkNotNull()
}

Я лично игнорирую результат checkNotNull(), как указано выше. И это делает вещи совместимыми с другими проверками, которые возвращают пустоту.

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

public String trim(String bar) {
    return Preconditions.checkNotNull(bar).trim();
}

Короче говоря, я согласен с вами, что API несколько противоречив, но я бы предпочел, чтобы все методы возвращали void. Метод должен либо иметь побочный эффект, либо что-то возвращать, но в целом следует избегать и того, и другого. Здесь целью метода является получение побочного эффекта: создание исключения.

РЕДАКТИРОВАТЬ:

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

person JB Nizet    schedule 29.07.2012
comment
Я бы не стал делать что-то вроде Preconditions.checkNotNull(bar).trim() вместо простого bar.trim(). В обоих случаях вы получаете один и тот же NPE, поэтому тест ничего вам не покупает, и вы только затемняете простую вещь. checkNotNull предназначен для быстрого отказа, поэтому вы избегаете возможного неправильного частичного выполнения метода и получаете более короткую трассировку стека. - person maaartinus; 29.07.2012
comment
Если вы напишете это как: return checkNotNull(bar, "bar may not be null").trim();, это станет иметь смысл. Во всяком случае, я не говорю, что это нужно делать. Наоборот: я думаю, что checkNotNull() должен возвращать void. - person JB Nizet; 29.07.2012
comment
@JBNizet Я уважаю качество вашей аргументации, поэтому я не буду минусовать. На мой взгляд, метод checknotNull — очень хороший выбор для возврата переменной, и я бы хотел, чтобы было больше таких методов (например, someInt / Ints.checkNoneZero(someOtherInt). Я думаю, что это хороший способ выделить важные требования к коду, не делая методы излишне длинными. Я думаю, что это делает код более, а не менее читабельно Напротив, я вообще не вижу смысла использовать метод в вашем первом примере. - person Sean Patrick Floyd; 30.07.2012
comment
У меня есть 1 вариант использования, где checkNotNull() возвращает свой аргумент. Вызов конструктора суперкласса: super(Preconditions.checkNotNull(param1)) - person adaslaw; 27.04.2021

Самая большая единственная причина, по которой checkNotNull возвращает свой аргумент, заключается в том, что его можно использовать в конструкторах, например:

public Foo(Bar bar) {
  this.bar = checkNotNull(bar);
}

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

person Louis Wasserman    schedule 29.07.2012
comment
Я не знал, что он вернул параметр... выполняя рефакторинг Code Golf :-) - person fommil; 31.07.2012

Вместо этого вы можете использовать valid4j с hamcrest-matchers (найдено на Maven Central как org.valid4j:valid4j)

Для предусловий и постусловий:

import static org.valid4j.Assertive.*;

this.myField = require(argument, notNullValue());
this.myInteger = require(x, greaterThan(0));
...
return ensure(result, isValid());

Для проверки ввода:

import static org.valid4j.Validation.*;


validate(argument, isValid(), otherwiseThrowing(InvalidException.class));

Ссылки:

person keyoxy    schedule 30.11.2014