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