Должен ли я написать нулевые проверки для всех моих методов? - PullRequest
1 голос
/ 17 апреля 2020

Я создаю свою собственную библиотеку, например Apache Commons DigestUtils для учебных целей.

Внутри моего update() метода я написал простую if (something == null) throw new Exception как null проверку.

/**
 * Performs a digest update for the {@code String} (converted to bytes using the
 * UTF-8 standard charsets) with a specific {@code MessageDigest} and returns
 * the final digest data.
 *
 *
 * @param messageDigest the {@code MessageDigest} with a specific algorithm to
 *                      process the digest.
 *
 * @param data          the data to digest.
 *
 * @return the {@code MessageDigest} with the processed update for the digest.
 *
 * @throws IllegalArgumentException if {@code messageDigest} is {@code null}.
 *
 * @throws IllegalArgumentException if {@code data} is {@code null}.
 */
public static MessageDigest update(final MessageDigest messageDigest, final String data) {
    if (messageDigest == null)
        throw new IllegalArgumentException("messageDigest cannot be null");
    if (data == null)
        throw new IllegalArgumentException("data cannot be null");

    messageDigest.update(data.getBytes(StandardCharsets.UTF_8));

    return messageDigest;
}

Теперь я не уверен, стоит ли мне писать такие же проверки в других методах, которые должны вызывать метод update(), или писать только в комментариях, что метод вызывает соответствующие исключения.

public static byte[] digest(final MessageDigest messageDigest, final byte[] data) {
    return update(messageDigest, data).digest();
}

или

public static byte[] digest(final MessageDigest messageDigest, final byte[] data) {
    if (messageDigest == null)
        throw new IllegalArgumentException("messageDigest cannot be null");
    if (data == null)
        throw new IllegalArgumentException("data cannot be null");

    return update(messageDigest, data).digest();
}

Обратите внимание, что digest() вызывает метод update() метод.

Какая лучшая практика?

Ответы [ 3 ]

0 голосов
/ 17 апреля 2020
  1. Начиная с JDK15 (или это уже в 14?), NPE, выбрасываемые самой системой (например, когда вы запускаете, скажем, Object o = null; o.hashCode();, будут иметь все детали, в частности текст выражение. Это означает, что если в вашем коде NPE будет происходить «естественно» (потому что вы разыменовываете переменную или передаете ее другому методу, который всегда это делает), нет нужды в проверке нуля. Стилистически проблема с этим если позже вы измените свой код так, чтобы он не обязательно разыменовывался, вы потеряете эффект, что вызов метода вызовет нулевой аргумент. Что-то нужно иметь в виду.

  2. Это По моему опыту, более распространенным явным образом является выброс «NullPointerException», т. е. запись if (param == null) throw new NullPointerException("param"). Различные библиотеки, которые автоматизируют этот процесс, также, как правило, по умолчанию используют NPE.

  3. Вам не нужно чтобы написать ядро ​​if. java поставляется с утилитой в одну строку: Objects.requireNonNull(param, "param"); намного короче вашего 3-строчного if блока!

  4. Y Вы можете использовать @NonNull от lombok для автоматической генерации этих нулевых проверок, что еще больше экономит время при наборе.

  5. Вы можете настроить IDE для предупреждения об изменении значений параметров; это означает, что вам больше не нужно указывать ключевое слово 'final' 15 950 951 (приблизительный) раз в ваших исходных файлах. Сохраняет много набора текста.

  6. В то время как другой ответ на этот вопрос до сих пор говорит, что вам не нужно , чтобы объявить throws IllegalArgumentException, что правда, делать это в целях документирования (опять же, по моему опыту) часто.

  7. Есть аннотации для пометки ненулевого значения. Помимо lombok, существуют различные библиотеки, которые дают вам нулевую проверку во время компиляции, и они обычно встроены в основные IDE, или есть плагин для них, так что вы получаете ошибки, которые вы пропускаете при передаче ( потенциальные) нулевые значения для методов, которые этого не хотят, или для выполнения бесполезных нулевых проверок (нулевые проверки, например, для значений, возвращаемых методами, которые указывают, что они никогда не возвращают нулевые значения). Если вы можете полагаться на то, что все пользователи библиотеки будут использовать такие инструменты, вам вообще не нужны проверки на ноль во время выполнения, но я бы посоветовал вам сохранить их; я думаю, вы не можете рассчитывать на то, что ваши пользователи получат эти инструменты. Тем не менее, рассмотрите возможность аннотировать ваши параметры, чтобы те, кто получит выгоду от проверок во время компиляции.

Для аннотаций во время компиляции у вас есть широкий выбор вариантов здесь (к сожалению, ): Есть checkerframework , который на сегодняшний день является самым обширным, позволяя вам go намного дальше, чем защитники нулевой проверки во время компиляции. Есть также наборы, запеченные в вашей IDE: eclipse и intellij - самые распространенные, но их гораздо больше.

Только lombok сгенерирует для вас нулевую проверку во время выполнения, и IDE можно настроить так, чтобы она учитывалась и для анализа во время компиляции, предоставляя вам лучшее из обоих миров. Также можно объединить одну из этих аннотаций каркаса с Objects.requireNonNull.

.
0 голосов
/ 17 апреля 2020

1.: Существует функция Objects.requireNonNull, предназначенная именно для этой цели. Выдает исключение, если заданный аргумент равен null.

if (messageDigest == null)
        throw new IllegalArgumentException("messageDigest cannot be null");

должно быть просто

Objects.requireNonNull(messageDigest, "messageDigest cannot be null");

2.: Лучшая практика - генерировать исключение потому что null означает бросить его , где null не должно происходить на первом месте . Это мотивация принципа Fail Fast .

Когда появляется ссылка null там, где она должна , а не , вы хотите бросить исключение или обработать его в точно в этом месте. Вы не хотите передать ссылку null другим методам / функциям. В противном случае вам будет сложнее отладить код, если переданная ссылка null вызовет проблемы в вашей программе. Это потому, что неясно, где ссылка null возникла в первую очередь; но то, что на самом деле вызвало , это то, что вы хотите исправить / обработать. Пример:

public class Demo {
    public void a(Object obj) {
        b(obj);
    }
    private void b(Object obj) {
        c(obj);
    }
    private void c(Object obj) {
        // Calculation using obj.
    }
}

Если ясно, что a никогда не получит ссылку null, это правильное место для проверки.

public void a(Object obj) {
    Objects.requireNonNull(obj); // Throw exception when null.
    b(obj);
}

Это плохо когда вы делаете проверку null в каждом отдельном методе, потому что тот факт, что obj не должно быть null, учитывается для всех этих методов, поэтому достаточно поместить проверку null там, где null не должно происходят в первую очередь, то есть a. Программа должна быстро провалиться .

Было бы еще хуже поставить null в c, потому что c логически никогда не получит ссылку null от b и b не должен получать один от a. Если ссылка null вызывает проблемы в c, вам будет сложнее выяснить, произошло ли null в b или a или коде, который вызвал a. Вы хотите, чтобы знали об этом, потому что вы хотите решить проблему возникающей ссылки null, , а не проблем, вызванных ею. Конечно, иногда вы не можете исправить сам случай, но вы можете попытаться подойти как можно ближе к этому событию и уменьшить побочный «ущерб», нанесенный им.

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

Надеюсь, это даст вам хорошее представление о том, как подойти к этому.

0 голосов
/ 17 апреля 2020

Лучше всего сделать так, чтобы метод update() выполнил свою задачу, а если он выдает Exception, вы также выбросите исключение. Есть две общие вещи, которые нужно сделать, обе выкинуть Exception, что вы тоже получите. Один из способов поместить throws SomeException в имя метода, где SomeException - это Exception, который вы исключаете.

public static byte[] digest(final MessageDigest messageDigest, final byte[] data) throws IllegalArgumentException {
    return update(messageDigest, data).digest();
}

Так что, если вы получите Exception из update(), это будет автоматически бросить это. ПРИМЕЧАНИЕ: вам не нужно бросать IllegalArgumentException, потому что это тип RunTimeException, которые автоматически выбрасываются. Второй способ - попытка перехватить, что дает вам немного больше возможностей.

public static byte[] digest(final MessageDigest messageDigest, final byte[] data) throws IllegalArgumentException {
    try {
        return update(messageDigest, data).digest();
    } catch(IllegalArgumentException e) {
        // Do whatever you want. You can even throw e if you want.
    }
}

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

...