До свидания, NullPointerException! Узнайте, какие передовые методы помогут избежать этого

Java

1. Краткое введение

Исключение нулевого указателя, с ним должны быть знакомы только все, кто писал бизнес-системы. Это ошибка времени выполнения, обычно вызванная неточной логикой и ленивым стилем кода. Причину этого понять несложно, но избежать ее — непростая задача. Ниже я записал некоторые приемы, которые я считаю лучшими, которые помогают мне избежать исключений нулевого указателя и косвенно улучшают качество моего кода и эффективность работы. Часть этого контента взята из книг, которые я читал на Kindle, а часть — из предложений StackOverflow и моего собственного опыта.Я записываю это здесь, надеясь вдохновить вас.

2. Определение проблемы

Предположим, такая бизнес-система: задача Task поддерживает последнюю запись выполнения Execution, а запись выполнения Execution поддерживает текущий результат выполнения Result. Вот определение интерфейса:


public interface Task {
    Execution getExecution();
}

public interface Execution {
    Result getResult();
}

public interface Result {
}

Теперь, когда задача получена из запроса к БД, нужно получить результат выполнения этого времени, если есть результат, делаем следующий шаг, иначе пропускаем его. Мы часто пишем так:

Task task = queryTaskFromDB();
if (task != null) {
    if (task.getExecution() != null) {
        if (task.getExecution().getResult() != null) {
            doSomethingOnResult(task.getExecution().getResult());
        }
    }
}

Чтобы быть логически строгими, мы должны оценить содержимое, полученное R в каждом CURD, что очень многословно.

В реальной разработке не так-то просто иметь возможность так дотошно и строго оценивать каждый полученный класс. Во многих случаях определения в бизнес-коде четко не разъясняются.Task::getExecutionМожно ли вернуть null: если это null, вернуть null или вернутьNoSuchElementExceptionШерстяная ткань? Это часто варьируется от реализации к реализации. Это также недружественная сторона Java, поскольку такой дизайн затрудняет полную реализацию интерфейсно-ориентированного программирования.

3. Некоторые общие рекомендации

3.1 Предложение № 0. Попробуйте модифицировать свой интерфейс с помощью опционального

Возьмем в качестве примера простую бизнес-систему в версии 2.1. Мы пытаемся использовать необязательный для преобразованияTaskиExecutionОпределение интерфейса:


public interface Task {
    Optional<Execution> getExecutionNullable();
}

public interface Execution {
    Optional<Result> getResultNullable();
}

public interface Result {
}

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

После завершения преобразования интерфейса, что мне делать, если я хочу запросить Задание из базы данных и получить результат его выполнения? Смотрите код ниже👇

Optional<Task> task = queryTaskFromDBNullable();
task
    .flatMap(Task::getExecutionNullable)
    .flatMap(Execution::getResultNullable)
    .ifPresent(this::doSomethingOnResult);

Ключом к этой обработке является Flatmap, и метод FLATMAP варьируется в зависимости от содержимого необязательных:

  • Если необязательный имеет значение, возьмите значение и назначьте его анонимной функции.
  • Если необязательный параметр не имеет значения, вернитеOptional.empty()

Сделав вышеописанное, вы сможете избежать громоздкого «кода лапши». :)

3.2 Некоторые другие предложения по развитию

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

3.2.1 Рекомендация №1 Используйте String.valueOf вместо toString

Object obj = null;
obj.toString(); // Ops,出错了

toString()является очень распространенным методом в разработке, но еслиobjБудет место для исключения нулевого указателя, тогда лучше использовать статический фабричный метод, чтобы гарантировать, что toString valueOf всегда будет успешным:

Object obj = null;
String.valueOf(obj); //如果为null 返回 "null"

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

3.2.2 Рекомендация №2 Добавить final к переменным, которые не изменяются после инициализации

String prompt = null;
int i = 50;
if (i < 50) {
    prompt = "too small";
} else if (i > 50) {
    prompt = "too large";
}
prompt.toString(); // Ops, 出错了

Если добавление final заставит вас инициализировать

final String prompt;
int i = 50;
if (i < 50) {
    prompt = "too small";
} else if (i > 50) {
    prompt = "too large";
} else {
    prompt = "you get it";
}
prompt.toString();

3.2.3 Рекомендация № 3 равносильна сравнению сначала значения, затем переменной

String var = null;
if (var.equals("test")) {
    doSomething(); // Ops, 出错了
}

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

String var = null;
if ("test".equals(var)) {
    doSomething(); 
}

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

4. Резюме

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