Веб-служба возвращает огромный XML, и мне нужно получить доступ к его глубоко вложенным полям. Например:
return wsObject.getFoo().getBar().getBaz().getInt()
Проблема заключается в том, что getFoo()
, getBar()
, getBaz()
может все возвращается null
.
Однако, если я проверю null
во всех случаях, код станет очень подробным и трудным для чтения. Более того, я могу пропустить проверки по некоторым полям.
if (wsObject.getFoo() == null) return -1;
if (wsObject.getFoo().getBar() == null) return -1;
// maybe also do something with wsObject.getFoo().getBar()
if (wsObject.getFoo().getBar().getBaz() == null) return -1;
return wsObject.getFoo().getBar().getBaz().getInt();
Допустимо ли писать
try {
return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
return -1;
}
или это будет считаться антипаттерном?
java
exception
nullpointerexception
null
custom-error-handling
Дэвид Франк
источник
источник
null
чеков, так какwsObject.getFoo().getBar().getBaz().getInt()
это уже запах кода. Прочтите, что такое «Закон Деметры» , и предпочтите соответственно реорганизовать свой код. Тогдаnull
исчезнет и проблема с чеками. И подумайте об использованииOptional
.wsdl2java
, что не соблюдает Закон Деметры.Ответы:
Отлов
NullPointerException
- действительно проблематичное занятие, поскольку оно может произойти практически где угодно. Очень легко получить один из бага, поймать его случайно и продолжить, как будто все в порядке, тем самым скрывая реальную проблему. С этим так сложно справиться, поэтому лучше избегать его вообще. (Например, подумайте об автоматической распаковке нуляInteger
.)Я предлагаю вам использовать
Optional
вместо этого класс. Часто это лучший подход, когда вы хотите работать с ценностями, которые либо присутствуют, либо отсутствуют.Используя это, вы можете написать свой код следующим образом:
Почему необязательно?
Использование
Optional
s вместоnull
значений, которые могут отсутствовать, делает этот факт очень заметным и понятным для читателей, а система типов гарантирует, что вы случайно не забудете об этом.Вы также получаете доступ к методам для более удобной работы с такими значениями, например,
map
иorElse
.Отсутствие действительно или ошибка?
Но также подумайте, является ли это допустимым результатом для промежуточных методов, возвращающих ноль, или это признак ошибки. Если это всегда ошибка, то, вероятно, лучше создать исключение, чем возвращать специальное значение, или чтобы сами промежуточные методы генерировали исключение.
Может еще варианты?
Если, с другой стороны, действительны отсутствующие значения из промежуточных методов, может быть, вы также можете переключиться на
Optional
s для них?Тогда вы можете использовать их так:
Почему не по желанию?
Единственная причина, по которой я могу не использовать,
Optional
- это если это действительно критичная для производительности часть кода, и если накладные расходы на сборку мусора оказываются проблемой. Это происходит потому , что несколькоOptional
объектов выделяются каждый раз , когда код выполняется, и виртуальная машина может не быть в состоянии оптимизировать те прочь. В этом случае ваши оригинальные if-тесты могут быть лучше.источник
Try
вместоOptional
. ХотяTry
в Java API его нет, есть много библиотек , предоставляющих его, например javaslang.io , github.com/bradleyscollins/try4j , functionjava.org или github.com/jasongoodwin/better-java-monadsFClass::getBar
и т.д. будет короче.static
метода, что повлечет за собой очень незначительные штрафы.Предлагаю подумать
Objects.requireNonNull(T obj, String message)
. Вы можете строить цепочки с подробным сообщением для каждого исключения, напримерЯ бы посоветовал вам не использовать специальные возвращаемые значения, например
-1
. Это не стиль Java. Java разработала механизм исключений, чтобы избежать этого старомодного способа, пришедшего из языка C.Метание
NullPointerException
тоже не лучший вариант. Вы можете указать собственное исключение (сделав его отмеченным, чтобы гарантировать, что оно будет обработано пользователем, или снимите флажок, чтобы обработать его более простым способом) или использовать конкретное исключение из используемого вами синтаксического анализатора XML.источник
Objects.requireNonNull
в итоге выкидываетNullPointerException
. Так что это не делает ситуацию иначе, чемreturn wsObject.getFoo().getBar().getBaz().getInt()
if
s, как показал OPOptional
класс или, может быть, вернуть значение, допускающее значение NULLInteger
Предполагая, что структура классов действительно находится вне нашего контроля, что, по-видимому, так и есть, я думаю, что обнаружение NPE, как предлагается в вопросе, действительно является разумным решением, если только производительность не является серьезной проблемой. Одно небольшое улучшение могло бы заключаться в том, чтобы обернуть логику throw / catch, чтобы избежать беспорядка:
Теперь вы можете просто:
источник
return get(() -> wsObject.getFoo().getBar().getBaz().getInt(), "");
не выдает ошибок во время компиляции, что может быть проблематичным.Как уже отмечал Том в комментарии,
Следующее заявление не подчиняется закону Деметры ,
Вы хотите,
int
и можете получить этоFoo
. Закон Деметры гласит: никогда не разговаривай с незнакомцами . В вашем случае вы можете скрыть реальную реализацию под капотомFoo
иBar
.Теперь вы можете создать метод в
Foo
извлечьint
изBaz
. В конечном счете,Foo
будет иметьBar
иBar
мы можем получить доступ ,Int
не подвергаяBaz
непосредственноFoo
. Таким образом, нулевые проверки, вероятно, разделены на разные классы, и только необходимые атрибуты будут разделяться между классами.источник
null
проверки своих собственных вложенных тегов.Мой ответ находится почти в той же строке, что и @janki, но я хотел бы немного изменить фрагмент кода, как показано ниже:
Вы также можете добавить нулевую проверку
wsObject
, если есть вероятность, что этот объект будет нулевым.источник
Вы говорите, что некоторые методы «могут возвращаться
null
», но не говорите, при каких обстоятельствах они возвращаютсяnull
. Вы говорите, что ловите это,NullPointerException
но не говорите, почему вы это ловите. Этот недостаток информации предполагает, что вы не имеете четкого представления о том, для чего нужны исключения и почему они лучше альтернативы.Рассмотрим метод класса, который предназначен для выполнения действия, но метод не может гарантировать, что он выполнит действие, из-за обстоятельств, не зависящих от него (что на самом деле имеет место для всех методов в Java ). Мы вызываем этот метод, и он возвращается. Код, вызывающий этот метод, должен знать, был ли он успешным. Откуда это знать? Как его можно структурировать, чтобы справиться с двумя возможностями - успехом или неудачей?
Используя исключения, мы можем написать методы, которые будут успешными как условие публикации . Если метод возвращается, он был успешным. Если он вызывает исключение, значит, он потерпел неудачу. Это большая победа для ясности. Мы можем написать код, который четко обрабатывает нормальный, успешный случай, и переместить весь код обработки ошибок в
catch
разделы. Часто выясняется, что подробности того, как и почему метод оказался неудачным, не важны для вызывающего, поэтому одно и то жеcatch
предложение можно использовать для обработки нескольких типов сбоев. И часто бывает , что метод не требует исключения улова на всех , но может просто позволить им распространяться на его вызывающий. Исключения из-за программных ошибок относятся к этому последнему классу; немногие методы могут адекватно реагировать на ошибку.Итак, те методы, которые возвращаются
null
.null
Указывает ли значение на ошибку в вашем коде? Если это так, вы вообще не должны перехватывать исключение. И ваш код не должен пытаться переубедить самого себя. Просто напишите то, что ясно и кратко, исходя из предположения, что это сработает. Является ли цепочка вызовов методов ясной и краткой? Тогда просто используйте их.null
Указывает ли значение на недопустимый ввод в вашу программу? Если это так, тоNullPointerException
исключение не следует генерировать, потому что обычно оно зарезервировано для индикации ошибок. Вероятно, вы захотите сгенерировать настраиваемое исключение, производное отIllegalArgumentException
(если вы хотите исключениеIOException
без проверки ) или (если вам нужно проверенное исключение). Требуется ли ваша программа для предоставления подробных сообщений об ошибках синтаксиса при недопустимом вводе? Если это так,null
то единственное, что вы можете сделать , - это проверка каждого метода на возвращаемое значение и выдача соответствующего диагностического исключения. Если вашей программе не требуется предоставлять подробную диагностику, объединение вызовов методов в цепочку, перехват любогоNullPointerException
и последующее генерирование пользовательского исключения является наиболее ясным и кратким.Один из ответов утверждает, что связанные вызовы методов нарушают закон Деметры и, следовательно, являются плохими. Это утверждение ошибочно.
источник
Чтобы улучшить читаемость, вы можете использовать несколько переменных, например
источник
Не лови
NullPointerException
. Вы не знаете, откуда он исходит (я знаю, что это маловероятно в вашем случае, но, возможно, что-то еще выбросило его), и это медленно. Вы хотите получить доступ к указанному полю, и для этого все остальные поля не должны быть пустыми. Это прекрасная веская причина проверить каждое поле. Я бы, наверное, проверил это в одном if, а затем создал бы метод для удобочитаемости. Как отмечали другие, уже возвращение -1 - это очень старая школа, но я не знаю, есть ли у вас для этого причина или нет (например, разговаривать с другой системой).Edit: Это спорно , если это disobeyes Закона Деметры , поскольку WsObject вероятно только структура данных (проверка https://stackoverflow.com/a/26021695/1528880 ).
источник
Если вы не хотите реорганизовывать код и можете использовать Java 8, можно использовать ссылки на методы.
Сначала простая демонстрация (извините за статические внутренние классы)
Вывод
Интерфейс
Getter
- это просто функциональный интерфейс, вы можете использовать любой эквивалент.GetterResult
class, аксессоры опущены для ясности, содержат результат цепочки геттеров, если таковые имеются, или индекс последнего вызванного геттера.Метод
getterChain
представляет собой простой шаблонный фрагмент кода, который может быть сгенерирован автоматически (или вручную при необходимости).Я структурировал код так, чтобы повторяющийся блок был очевиден.
Это не идеальное решение, поскольку вам все равно нужно определять одну перегрузку для
getterChain
каждого количества геттеров.Вместо этого я бы реорганизовал код, но если не получается, и вы часто обнаруживаете, что используете длинные цепочки геттеров, вы можете подумать о создании класса с перегрузками, которые занимают от 2 до, скажем, 10 геттеров.
источник
Как уже говорили другие, соблюдение Закона Деметры, безусловно, является частью решения. Другая часть, где это возможно, - изменить эти связанные методы, чтобы они не могли вернуться
null
. Вы можете избежать возвратаnull
, вместо этого возвращая пустойString
, пустойCollection
или какой-либо другой фиктивный объект, который означает или делает то, что вызывающий будет делатьnull
.источник
Я хотел бы добавить ответ, в котором основное внимание уделяется значению ошибки . Нулевое исключение само по себе не дает никакого смысла полной ошибки. Поэтому я бы посоветовал не иметь с ними дела напрямую.
Есть тысячи случаев, когда ваш код может пойти не так: не удается подключиться к базе данных, исключение ввода-вывода, ошибка сети ... Если вы разбираетесь с ними один за другим (например, с нулевой проверкой здесь), это было бы слишком хлопотно.
В коде:
Даже если вы знаете, какое поле имеет значение NULL, вы не знаете, что пошло не так. Может быть, Bar равен нулю, но ожидается ли это? Или это ошибка данных? Подумайте о людях, которые читают ваш код
Как и в ответе xenteros, я бы предложил использовать настраиваемое исключение без проверки . Например, в этой ситуации: Foo может быть нулевым (допустимые данные), но Bar и Baz никогда не должны быть нулевыми (недопустимые данные)
Код можно переписать:
источник
NullPointerException
является исключением во время выполнения, поэтому, вообще говоря, не рекомендуется его ловить, но следует избегать его.Вам нужно будет перехватить исключение везде, где вы хотите вызвать метод (или оно будет распространяться вверх по стеку). Тем не менее, если в вашем случае вы можете продолжать работать с этим результатом со значением -1, и вы уверены, что он не будет распространяться, потому что вы не используете какие-либо «части», которые могут быть нулевыми, то мне кажется правильным Лови
Редактировать:
Я согласен с более поздним ответом от @xenteros, лучше будет запустить собственное исключение, вместо того, чтобы возвращать -1, вы можете его назвать,
InvalidXMLException
например.источник
Подписываюсь на этот пост со вчерашнего дня.
Я комментировал / голосовал за комментарии, в которых говорится, что ловить NPE - это плохо. Вот почему я это делаю.
Вывод
3,216
0,002
Я вижу здесь явного победителя. Наличие проверок if намного дешевле, чем перехват исключения. Я видел, как это делается в Java-8. Учитывая, что 70% текущих приложений все еще работают на Java-7, я добавляю этот ответ.
Практический результат Для любых критически важных приложений обработка NPE стоит дорого.
источник
Если эффективность является проблемой, следует рассмотреть вариант «улова». Если 'catch' нельзя использовать, потому что он будет распространяться (как указано в 'SCouto'), тогда используйте локальные переменные, чтобы избежать множественных вызовов методов
getFoo()
,getBar()
иgetBaz()
.источник
Стоит подумать о создании собственного исключения. Назовем это MyOperationFailedException. Вы можете выбросить его вместо того, чтобы вернуть значение. Результат будет таким же - вы выйдете из функции, но не вернете жестко запрограммированное значение -1, которое является антипаттерном Java. В Java мы используем исключения.
РЕДАКТИРОВАТЬ:
По результатам обсуждения в комментариях позвольте мне добавить кое-что к своим предыдущим мыслям. В этом коде есть две возможности. Во-первых, вы принимаете значение null, а во-вторых, что это ошибка.
Если это ошибка, и она возникает, вы можете отладить свой код, используя другие структуры для целей отладки, когда точек останова недостаточно.
Если это приемлемо, вам все равно, где появился этот нуль. Если вы это сделаете, вам определенно не следует связывать эти запросы.
источник
У вас есть длинный, но очень читаемый метод. Если бы я был новым разработчиком, пришедшим к вашей кодовой базе, я мог бы довольно быстро увидеть, что вы делаете. Большинство других ответов (включая перехват исключения), похоже, не делают вещи более читаемыми, а некоторые, на мой взгляд, делают их менее читаемыми.
Учитывая, что вы, вероятно, не контролируете сгенерированный источник, и предполагая, что вам действительно нужно получить доступ к нескольким глубоко вложенным полям здесь и там, я бы рекомендовал обернуть каждый глубоко вложенный доступ методом.
Если вы обнаружите, что пишете много этих методов или если у вас возникнет соблазн сделать эти общедоступные статические методы, я бы создал отдельную объектную модель, вложенную, как вы хотите, только с теми полями, которые вам нужны, и преобразовал бы из Интернета объектную модель services в вашу объектную модель.
Когда вы общаетесь с удаленной веб-службой, очень типично иметь «удаленный домен» и «домен приложения» и переключаться между ними. Удаленный домен часто ограничен веб-протоколом (например, вы не можете отправлять вспомогательные методы туда и обратно в чистой службе RESTful, а глубоко вложенные объектные модели являются обычным явлением, чтобы избежать множественных вызовов API) и поэтому не идеальны для прямого использования в ваш клиент.
Например:
источник
применяя Закон Деметры,
источник
Давать ответ, который кажется отличным от всех остальных.
Причина:
Чтобы упростить чтение кода, попробуйте это для проверки условий:
РЕДАКТИРОВАТЬ :
Любые предложения будут оценены .. !!
источник
wsObject
будет содержать значение, возвращаемое веб-сервисом .. !! Сервис уже будет вызван иwsObject
получит длинныеXML
данные в качестве ответа веб- сервиса .. !! Таким образом, нет ничего лучше сервера, расположенного на другом континенте, потому чтоgetFoo()
это просто элемент, получающий метод получения, а не вызов веб-службы .. !! @xenterosЯ написал класс,
Snag
который позволяет вам определять путь для навигации по дереву объектов. Вот пример его использования:Это означает, что экземпляр
ENGINE_NAME
будет эффективно вызыватьCar?.getEngine()?.getName()
переданный ему экземпляр и возвращать его,null
если возвращается какая-либо ссылкаnull
:Он не опубликован на Maven, но если кому-то это пригодится, он здесь (конечно, без гарантии!)
Это немного простовато, но, похоже, работает. Очевидно, что он более устарел с более поздними версиями Java и других языков JVM, которые поддерживают безопасную навигацию или
Optional
.источник