Изящно избегая исключения NullPointerException в Java
-
12-09-2019 - |
Вопрос
Рассмотрим эту строку:
if (object.getAttribute("someAttr").equals("true")) { // ....
Очевидно, что эта строка является потенциальной ошибкой, атрибут может быть null
и мы получим NullPointerException
.Поэтому нам нужно реорганизовать его на один из двух вариантов:
Первый вариант:
if ("true".equals(object.getAttribute("someAttr"))) { // ....
Второй вариант:
String attr = object.getAttribute("someAttr");
if (attr != null) {
if (attr.equals("true")) { // ....
Первый вариант неудобен для чтения, но более лаконичен, в то время как второй ясен по замыслу, но многословен.
Какой вариант вы предпочитаете с точки зрения удобочитаемости?
Решение
Я всегда использовал
if ("true".equals(object.getAttribute("someAttr"))) { // ....
потому что, хотя это немного сложнее читать, это гораздо менее многословно, и я думаю, что это достаточно читабельно, так что вы очень легко к этому привыкаете
Другие советы
Во втором варианте вы можете воспользоваться преимуществами короткого замыкания &&
:
String attr = object.getAttribute("someAttr");
if (attr != null && attr.equals("true")) { // ....
Бывают определенные ситуации, когда сжатый подход кажется неправильным с самого начала, но эффективным становится идиоматический.Это один из них;другой - это что-то вроде:
String line;
while ((line = bufferedReader.readLine()) != null) {
// Use line
}
Побочные эффекты в каком-то состоянии?Немыслимо!За исключением того, что это в принципе приятнее, чем альтернативные варианты, когда вы распознаете конкретный шаблон.
Этот шаблон похож - он настолько распространен в Java, что я ожидаю, что любой достаточно опытный разработчик распознает его.Результат получается приятно лаконичным.(Забавно, но иногда я вижу код C #, который без необходимости использует ту же идиому - оператор равенства прекрасно работает со строками в C #.)
Итог:используйте первую версию и ознакомьтесь с ней.
Мне нравится вариант 1, и я бы сказал, что он достаточно читабелен.
Кстати, вариантом 3 было бы ввести метод getAttribute, который принимает значение по умолчанию в качестве параметра.
Всегда стремитесь к более короткому коду, учитывая, что оба они функционально эквивалентны.Особенно в подобном случае, когда удобочитаемость не приносится в жертву.
Util.isEmpty(string)
- возвращает string == null || string.trim().isEmpty()
Util.notNull(string)
возвращает "" если string == null
, строка в противном случае.Util.isNotEmpty(string)
возвращается ! Util.isEmpty(string)
И у нас есть соглашение, что для строк, Util.isEmpty(string)
семантически означает истинный и Util.isNotEmpty(string)
семантически означает false.
Это очень хороший вопрос.Обычно я использую не изящный:
if (object.getAttribute("someAttr") != null && object.getAttribute("someAttr").equals("true")) { // ....
(и я больше не буду им пользоваться)
У меня есть другой ответ;
List<Map<String, Object>> group = jjDatabase.separateRow(db.Select("SELECT * FROM access_user_group WHERE user_id=1 ;"));
в моей базе данных нет "group_c80" в качестве столбца в 'access_user_group', поэтому в get(0).get("group_c80") соответствует исключение нулевого указателя.Но я справился с этим с помощью приведенного ниже кода :
for (int j = 1; j < 100; j++) {
String rulId="0";//defult value,to privent null pointer exeption in group_c
try {
rulId = group.get(0).get("group_c" + j)).toString();
} catch (Exception ex) {
ServerLog.Print( "Handeled error in database for " + "group_c" + (j < 10 ? "0" + j : j) +"This error handeled and mot efect in program");
rulId = "0";
}}
Вот мой подход, требующий PropertyUtil
класс, хотя, но он написан только один раз:
/**
* Generic method to encapsulate type casting and preventing nullPointers.
*
* @param <T> The Type expected from the result value.
* @param o The object to cast.
* @param typedDefault The default value, should be of Type T.
*
* @return Type casted o, of default.
*/
public static <T> T getOrDefault (Object o, T typedDefault) {
if (null == o) {
return typedDefault;
}
return (T) o;
}
Клиентский код может это сделать:
PropertyUtil.getOrDefault(obj.getAttribute("someAttr"), "").equals("true");
или, для списка:
PropertyUtil.getOrDefault(
genericObjectMap.get(MY_LIST_KEY), Collections.EMPTY_LIST
).contains(element);
Или потребителю списка, который отклонит объект:
consumeOnlyList(
PropertyUtil.getOrDefault(
enericObjectMap.get(MY_LIST_KEY), Collections.EMPTY_LIST
)
)
Значением по умолчанию может быть impl шаблона null object https://en.wikipedia.org/wiki/Null_Object_pattern