Подтвердить что ты не робот

Какой смысл Guava checkNotNull

Я новичок в Guava (пусть честно, я не "довольно новый", я полный новичок в этом вопросе), и поэтому я решил ознакомиться с некоторыми документами и очень изумлен, читая это

com.google.common.base.Preconditions.checkNotNull(...)

Я не понимаю смысл этого метода. Это означает, что вместо выполнения:

myObject.getAnything();

(что может привести к NullPointerException, если myObject имеет значение null)

Я должен использовать

checkNotNull(myObject).getAnything();

который будет бросать NullPointerException, если myObject имеет значение null и возвращает myObject, если он не является нулевым.

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

В чем смысл этого? Эти две линии делают то же самое, что и для результатов, учитывая любые ситуации, о которых я могу думать.

Я даже не думаю, что последнее более читаемо.

Поэтому я должен что-то упустить. Что это?

4b9b3361

Ответ 1

Идея состоит в том, чтобы быстро провалиться. Например, рассмотрите этот глупый класс:

public class Foo {
    private final String s;

    public Foo(String s) {
        this.s = s;
    }

    public int getStringLength() {
        return s.length();
    }
}

Скажем, вы не хотите разрешать нулевые значения для s. (иначе getStringLength будет бросать NPE). С классом as-is, к тому времени, когда вы поймете, что null, это слишком поздно - очень сложно выяснить, кто его там положил. Преступник вполне мог бы быть в совершенно другом классе, и что пример Foo мог быть построен давно. Теперь вам нужно расчесывать свою базу кода, чтобы узнать, кто мог бы поместить там значение null.

Вместо этого представьте этот конструктор:

public Foo(String s) {
    this.s = checkNotNull(s);
}

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


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

public class StringLengthAverager {
    private int stringsSeen;
    private int totalLengthSeen;

    public void accept(String s) {
        stringsSeen++;
        totalLengthSeen += s.length();
    }

    public double getAverageLength() {
        return ((double)totalLengthSeen) / stringsSeen;
    }
}

Вызов accept(null) приведет к тому, что NPE будет сброшен - но не раньше, чем stringsSeen был увеличен. Возможно, это не то, что вы хотите; как пользователь класса, я могу ожидать, что если он не принимает значения NULL, то его состояние не должно изменяться, если вы передадите нуль (другими словами: вызов должен завершиться неудачно, но он не должен лишить объект). Очевидно, что в этом примере вы также можете исправить это, получив s.length() до увеличения stringsSeen, но вы можете увидеть, как для более длинного и более сложного метода, может быть полезно сначала проверить, что все ваши аргументы действительны, и только затем изменить состояние:

    public void accept(String s) {
        checkNotNull(s); // that is, s != null is a precondition of the method

        stringsSeen++;
        totalLengthSeen += s.length();
    }

Ответ 2

myObject.getAnything(); (что может вызвать исключение NullPointerException, если myObject имеет значение null)

Нет... он будет бросать NPE всякий раз, когда myObject == null. В Java нет возможности вызвать метод с приемником null (теоретическое исключение - это статические методы, но они могут и должны всегда вызываться без какого-либо объекта).


Я должен использовать checkNotNull(myObject).getAnything();

Нет, не стоит. Это будет довольно избыточным (Обновить).

Вы должны использовать checkNotNull для быстрого сбоя. Без этого вы можете передать незаконный null другому методу, который передает его дальше и т.д. И т.д., Где он, наконец, терпит неудачу. Тогда вам может понадобиться удача, чтобы узнать, что на самом деле первый метод должен был отклонить null.


Ответ yshavit указывает на важный момент: передача незаконного значения плохое, но сохранение его и его передача позже еще хуже.

Update

На самом деле,

 checkNotNull(myObject).getAnything()

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

 myObject != null ? myObject.getAnything() : somethingElse

OTOH, я не думаю, что проверка стоит многословия. В лучший язык система типов рассмотрит значение nullability и даст нам некоторый семантический сахар, например

 myObject!!.getAnything()                    // checkNotNull
 myObject?.getAnything()                     // safe call else null
 myObject?.getAnything() ?: somethingElse    // safe call else somethingElse

для nullable myObject, в то время как стандартный синтаксис точки будет разрешен только тогда, когда myObject, как известно, не является нулевым.

Ответ 3

Я прочитал всю эту цепочку несколько минут назад. Тем не менее я был смущен, почему мы должны использовать checkNotNull. Затем просмотрите документ класса Precondition из Guava, и я получил то, что ожидал. Чрезмерное использование checkNotNull приведет к деградации производительности.

Мне кажется, что метод checkNotNull необходим для проверки данных, которая исходит от пользователя напрямую или очень конца API для взаимодействия с пользователем. Он не должен использоваться во всех методах внутреннего API, потому что, используя его, вы не можете остановить исключение, скорее исправьте свой внутренний API, чтобы избежать исключения.

В соответствии с DOC: Ссылка

Использование checkNotNull:

public static double sqrt(double value) {
     Preconditions.checkArgument(value >= 0.0, "negative value: %s", value);
     // calculate the square root
}

Предупреждение о производительности

Цель этого класса - улучшить читаемость кода, но в некоторых обстоятельства могут иметь значительную стоимость исполнения. Помните, что значения параметров для построения сообщений должны быть вычислено с нетерпением, а создание автобоксинга и массива varargs может произойти также, даже когда проверка предварительного условия затем выполняется (как и должно быть почти всегда в производстве). В некоторых случаях эти потери Процессные циклы и распределения могут создавать реальную проблему. Проверки предварительных условий, чувствительные к производительности, всегда могут быть преобразованы в обычная форма:

if (value < 0.0) {
     throw new IllegalArgumentException("negative value: " + value);
}