Нет ни одного правильного порядка. Выберите систему и придерживайтесь ее. Я использую:
class SomeClass(object):
def __magic_methods__(self):
"magic methods first, usually in alphabetical order"
def _private_method(self):
"worker methods next, also in alpha order"
def a_method(self):
"then normal methods, also in alpha order"
Ловить NullPointerException
- действительно проблематичная вещь, потому что они могут случиться почти где угодно. Очень легко получить один из ошибок, поймать его случайно и продолжить, как будто все нормально, что скрывает настоящую проблему. Сложно справиться с этим, поэтому лучше всего избегать. (Например, подумайте об авто-распаковке нулевого Integer
.)
Вместо этого я рекомендую вам использовать класс Optional
. Это часто лучший подход, когда вы хотите работать со значениями, которые присутствуют или отсутствуют.
Используя это, вы можете написать свой код следующим образом:
public Optional<Integer> m(Ws wsObject) {
return Optional.ofNullable(wsObject.getFoo()) // Here you get Optional.empty() if the Foo is null
.map(f -> f.getBar()) // Here you transform the optional or get empty if the Bar is null
.map(b -> b.getBaz())
.map(b -> b.getInt());
// Add this if you want to return an -1 int instead of an empty optional if any is null
// .orElse(-1);
// Or this if you want to throw an exception instead
// .orElseThrow(SomeApplicationException::new);
}
Использование Optional
s вместо null
для значений, которые могут отсутствовать, делает этот факт очень понятным и понятным для читателей, а система типов гарантирует, что вы не случайно забыли о это.
Вы также получаете доступ к методам работы с такими значениями более удобно, например map
и orElse
.
Но также подумайте, является ли это допустимым результатом для промежуточных методов для возврата null или если это признак ошибки. Если это всегда ошибка, то, вероятно, лучше исключить исключение, чем возвращать специальное значение, или для самих промежуточных методов вызывать исключение.
Если, с другой стороны, отсутствующие значения из промежуточных методов действительны, возможно, вы также можете переключиться на Optional
s для них?
Тогда вы могли бы использовать их следующим образом:
public Optional<Integer> mo(Ws wsObject) {
return wsObject.getFoo()
.flatMap(f -> f.getBar())
.flatMap(b -> b.getBaz())
.flatMap(b -> b.getInt());
}
Единственная причина, по которой я могу думать, f11], если это действительно критически важная часть кода, и если накладные расходы на сбор мусора оказываются проблемой. Это связано с тем, что при каждом запуске кода выделяется несколько объектов Optional
, и VM может не быть в состоянии оптимизировать их. В этом случае ваши оригинальные if-тесты могут быть лучше.
Предлагаю рассмотреть Objects.requireNonNull(T obj, String message)
. Вы можете создавать цепочки с подробным сообщением для каждого исключения, например
requireNonNull(requireNonNull(requireNonNull(
wsObject, "wsObject is null")
.getFoo(), "getFoo() is null")
.getBar(), "getBar() is null");
. Я предлагаю вам не использовать специальные возвращаемые значения, например -1
. Это не стиль Java. Java разработала механизм исключений, чтобы избежать этого старомодного способа, который исходил с языка C.
Бросок NullPointerException
тоже не самый лучший вариант. Вы можете предоставить свое собственное исключение (сделав его проверенным , чтобы гарантировать, что он будет обработан пользователем или непроверенным , чтобы обработать его более простым способом) или использовать конкретное исключение из XML-парсер, который вы используете.
Objects.requireNonNull
в итоге выбрасывает NullPointerException
. Таким образом, это не делает ситуацию иной, чем return wsObject.getFoo().getBar().getBaz().getInt()
– Arka Ghosh
22 June 2016 в 07:18
if
s, поскольку OP показал
– Andrew Tobilko
22 June 2016 в 07:25
Optional
или, возможно, вернуть значение с нулевым значением Integer
– Lii
22 June 2016 в 09:35
Мой ответ идет почти в той же строке, что и @janki, но я хотел бы немного изменить фрагмент кода, как показано ниже:
if (wsObject.getFoo() != null && wsObject.getFoo().getBar() != null && wsObject.getFoo().getBar().getBaz() != null)
return wsObject.getFoo().getBar().getBaz().getInt();
else
return something or throw exception;
Вы также можете добавить нулевую проверку для wsObject
, если есть вероятность, что этот объект будет null.
NullPointerException
- исключение во время выполнения, поэтому, вообще говоря, не рекомендуется его ловить, но чтобы избежать этого.
Вам придется поймать исключение, где бы вы ни называли метод (или он будет распространяться по стеку). Тем не менее, если в вашем случае вы можете продолжать работать с этим результатом со значением -1, и вы уверены, что он не будет распространяться, потому что вы не используете ни одну из «частей», которая может быть нулевой, тогда мне кажется правильным
Изменить:
Я согласен с более поздним ответом из @xenteros, лучше запустить собственное исключение, вместо этого возвращая -1, вы можете назовите его InvalidXMLException
, например.
null
своих собственных подтеков.
– Tom
22 June 2016 в 09:45
Я хотел бы добавить ответ, в котором основное внимание будет уделено значению ошибки. Исключительное исключение само по себе не дает никакого значения полной ошибки. Поэтому я бы посоветовал не обращаться к ним напрямую.
Существует тысячи случаев, когда ваш код может пойти не так: невозможно подключиться к базе данных, исключению IO, сетевой ошибке ... Если вы справляетесь с ними,
В коде:
wsObject.getFoo().getBar().getBaz().getInt();
Даже если вы знаете, какое поле имеет значение null, вы не имеют представления о том, что пошло не так. Может быть, Bar имеет значение NULL, но ожидаем? Или это ошибка данных? Подумайте о людях, которые читают ваш код
Как и в ответе xenteros, я бы предложил использовать выборочное исключение без проверки. Например, в этой ситуации: Foo может быть нулевым (действительные данные), но Bar и Baz никогда не должны быть нулевыми (недопустимыми данными)
Код может быть переписан:
void myFunction()
{
try
{
if (wsObject.getFoo() == null)
{
throw new FooNotExistException();
}
return wsObject.getFoo().getBar().getBaz().getInt();
}
catch (Exception ex)
{
log.error(ex.Message, ex); // Write log to track whatever exception happening
throw new OperationFailedException("The requested operation failed")
}
}
void Main()
{
try
{
myFunction();
}
catch(FooNotExistException)
{
// Show error: "Your foo does not exist, please check"
}
catch(OperationFailedException)
{
// Show error: "Operation failed, please contact our support"
}
}
Предоставление ответа, который кажется отличным от всех остальных.
Я рекомендую вам проверить
blockquote>NULL
вif
s.Причина:
Мы не должны оставлять шанс, чтобы наша программа была разбита. NullPointer генерируется системой. Поведение генерируемых системой исключений невозможно предсказать. Вы не должны оставлять свою программу в руках Системы, когда у вас уже есть способ ее обработки самостоятельно. И добавьте механизм обработки исключений для дополнительной безопасности. !!
blockquote>Для упрощения чтения кода проверьте это для проверки условий:
if (wsObject.getFoo() == null || wsObject.getFoo().getBar() == null || wsObject.getFoo().getBar().getBaz() == null) return -1; else return wsObject.getFoo().getBar().getBaz().getInt();
EDIT:
Здесь вам нужно сохранить эти значения
blockquote>wsObject.getFoo()
,wsObject.getFoo().getBar()
,wsObject.getFoo().getBar().getBaz()
в некоторых переменных. Я не делаю этого, потому что я не знаю возвращаемых типов этих функций.Любые предложения будут оценены .. !!
wsObject
будет содержать значение, возвращаемое из Webservice .. !! Служба будет вызвана уже, и wsObject
получит длинные данные XML
как ответ webservice .. !! Таким образом, нет ничего похожего на сервер , расположенный на другом континенте , потому что getFoo()
- это просто элемент, получающий метод getter , а не вызов Webservice .. !! @xenteros
– Janki Gadhiya
22 June 2016 в 07:33
Если эффективность является проблемой, следует рассмотреть вариант «улов». Если «catch» не может использоваться, потому что он будет распространяться (как упоминается «SCouto»), используйте локальные переменные, чтобы избежать множественных вызовов методов getFoo()
, getBar()
и getBaz()
.
Чтобы улучшить читаемость, вы можете использовать несколько переменных, например
Foo theFoo;
Bar theBar;
Baz theBaz;
theFoo = wsObject.getFoo();
if ( theFoo == null ) {
// Exit.
}
theBar = theFoo.getBar();
if ( theBar == null ) {
// Exit.
}
theBaz = theBar.getBaz();
if ( theBaz == null ) {
// Exit.
}
return theBaz.getInt();
Как говорили другие, уважение Закона Деметры, безусловно, является частью решения. Другая часть, где это возможно, заключается в изменении этих цепочечных методов, чтобы они не могли вернуться null
. Вы можете не возвращать null
, вместо этого возвращая пустой String
, пустой Collection
или какой-либо другой фиктивный объект, который означает или делает то, что вызывающий абонент будет делать с null
.
return wsObject.getFooBarBazInt();
, применяя Закон Деметры,
class WsObject
{
FooObject foo;
..
Integer getFooBarBazInt()
{
if(foo != null) return foo.getBarBazInt();
else return null;
}
}
class FooObject
{
BarObject bar;
..
Integer getBarBazInt()
{
if(bar != null) return bar.getBazInt();
else return null;
}
}
class BarObject
{
BazObject baz;
..
Integer getBazInt()
{
if(baz != null) return baz.getInt();
else return null;
}
}
class BazObject
{
Integer myInt;
..
Integer getInt()
{
return myInt;
}
}
Если вы не хотите реорганизовывать код, и вы можете использовать Java 8, можно использовать ссылки на методы.
Простая демонстрация сначала (извините статические внутренние классы)
public class JavaApplication14
{
static class Baz
{
private final int _int;
public Baz(int value){ _int = value; }
public int getInt(){ return _int; }
}
static class Bar
{
private final Baz _baz;
public Bar(Baz baz){ _baz = baz; }
public Baz getBar(){ return _baz; }
}
static class Foo
{
private final Bar _bar;
public Foo(Bar bar){ _bar = bar; }
public Bar getBar(){ return _bar; }
}
static class WSObject
{
private final Foo _foo;
public WSObject(Foo foo){ _foo = foo; }
public Foo getFoo(){ return _foo; }
}
interface Getter<T, R>
{
R get(T value);
}
static class GetterResult<R>
{
public R result;
public int lastIndex;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
WSObject wsObject = new WSObject(new Foo(new Bar(new Baz(241))));
WSObject wsObjectNull = new WSObject(new Foo(null));
GetterResult<Integer> intResult
= getterChain(wsObject, WSObject::getFoo, Foo::getBar, Bar::getBar, Baz::getInt);
GetterResult<Integer> intResult2
= getterChain(wsObjectNull, WSObject::getFoo, Foo::getBar, Bar::getBar, Baz::getInt);
System.out.println(intResult.result);
System.out.println(intResult.lastIndex);
System.out.println();
System.out.println(intResult2.result);
System.out.println(intResult2.lastIndex);
// TODO code application logic here
}
public static <R, V1, V2, V3, V4> GetterResult<R>
getterChain(V1 value, Getter<V1, V2> g1, Getter<V2, V3> g2, Getter<V3, V4> g3, Getter<V4, R> g4)
{
GetterResult result = new GetterResult<>();
Object tmp = value;
if (tmp == null)
return result;
tmp = g1.get((V1)tmp);
result.lastIndex++;
if (tmp == null)
return result;
tmp = g2.get((V2)tmp);
result.lastIndex++;
if (tmp == null)
return result;
tmp = g3.get((V3)tmp);
result.lastIndex++;
if (tmp == null)
return result;
tmp = g4.get((V4)tmp);
result.lastIndex++;
result.result = (R)tmp;
return result;
}
}
Выход
241 4
null 2
blockquote>Интерфейс
Getter
- это просто функциональный интерфейс, вы можете использовать любой эквивалент.GetterResult
класс, аксессоры, выделенные для ясности, удерживают результат геттерной цепи, если таковой имеется, или индекс последнего геттера.Метод
getterChain
- это простая, шаблонная часть кода, которая может быть сгенерирована автоматически (или вручную при необходимости). Я структурировал код так, чтобы повторяющийся блок был само собой разумеющимся.
Это не идеальное решение, так как вам по-прежнему необходимо определить одну перегрузку
getterChain
на количество геттеров.Я бы реорганизовал код вместо этого, но если не удается, и вы часто используете свои длинные геттерные цепи, вы можете подумать о создании класса с перегрузками, которые берут от 2 до, скажем, 10, getters.
Следили за этим сообщением со вчерашнего дня.
Я комментирую / голосую комментарии, которые говорят, что улавливать NPE плохо. Вот почему я это делал.
package com.todelete;
public class Test {
public static void main(String[] args) {
Address address = new Address();
address.setSomeCrap(null);
Person person = new Person();
person.setAddress(address);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
try {
System.out.println(person.getAddress().getSomeCrap().getCrap());
} catch (NullPointerException npe) {
}
}
long endTime = System.currentTimeMillis();
System.out.println((endTime - startTime) / 1000F);
long startTime1 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
if (person != null) {
Address address1 = person.getAddress();
if (address1 != null) {
SomeCrap someCrap2 = address1.getSomeCrap();
if (someCrap2 != null) {
System.out.println(someCrap2.getCrap());
}
}
}
}
long endTime1 = System.currentTimeMillis();
System.out.println((endTime1 - startTime1) / 1000F);
}
}
public class Person {
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
package com.todelete;
public class Address {
private SomeCrap someCrap;
public SomeCrap getSomeCrap() {
return someCrap;
}
public void setSomeCrap(SomeCrap someCrap) {
this.someCrap = someCrap;
}
}
package com.todelete;
public class SomeCrap {
private String crap;
public String getCrap() {
return crap;
}
public void setCrap(String crap) {
this.crap = crap;
}
}
Выход
3.216
0.002
Я вижу здесь явного победителя. Если проверка слишком дешевле, чем исключение. Я видел этот способ Java-8. Учитывая, что 70% текущих приложений по-прежнему работают на Java-7, я добавляю этот ответ.
Bottom Line Для любых критически важных приложений обработка NPE является дорогостоящей.
Метод, который у вас есть, длинный, но очень читаемый. Если бы я был новым разработчиком, который приходил на вашу базу кода, я мог бы видеть, что вы делаете довольно быстро. На мой взгляд, большинство других ответов (в том числе и для исключения исключения) не делают вещи более читабельными, а некоторые делают их менее читаемыми.
Учитывая, что вы, вероятно, не имеете контроля над сгенерированный источник и предполагая, что вам действительно нужно получить доступ к нескольким глубоко вложенным полям здесь и там, я бы рекомендовал обернуть каждый глубоко вложенный доступ с помощью метода.
private int getFooBarBazInt() {
if (wsObject.getFoo() == null) return -1;
if (wsObject.getFoo().getBar() == null) return -1;
if (wsObject.getFoo().getBar().getBaz() == null) return -1;
return wsObject.getFoo().getBar().getBaz().getInt();
}
Если вы обнаружите, что пишете много таких методы, или если у вас возникли соблазн сделать эти общедоступные статические методы, тогда я бы создал отдельную объектную модель, вложенную, как вам хотелось бы, только с полями, которые вам нужны, и конвертировать из объектной модели веб-сервисов в вашу объектную модель.
Когда вы общаетесь с удаленной веб-службой, очень типично иметь «удаленный домен» и «домен приложения» и переключаться между ними. Удаленный домен часто ограничивается веб-протоколом (например, вы не можете отправлять вспомогательные методы назад и вперед в чистом RESTful-сервисе, а глубоко вложенные объектные модели являются общими, чтобы избежать множества вызовов API), и поэтому они не идеальны для прямого использования в ваш клиент.
Например:
public static class MyFoo {
private int barBazInt;
public MyFoo(Foo foo) {
this.barBazInt = parseBarBazInt();
}
public int getBarBazInt() {
return barBazInt;
}
private int parseFooBarBazInt(Foo foo) {
if (foo() == null) return -1;
if (foo().getBar() == null) return -1;
if (foo().getBar().getBaz() == null) return -1;
return foo().getBar().getBaz().getInt();
}
}
Я написал класс под названием Snag
, который позволяет вам определить путь для навигации по дереву объектов. Вот пример его использования:
Snag<Car, String> ENGINE_NAME = Snag.createForAndReturn(Car.class, String.class).toGet("engine.name").andReturnNullIfMissing();
Значение того, что экземпляр ENGINE_NAME
эффективно вызовет Car?.getEngine()?.getName()
в экземпляре, переданном ему, и вернет null
, если какая-либо ссылка вернулась null
:
final String name = ENGINE_NAME.get(firstCar);
Это не опубликовано на Maven, но если кто-нибудь найдет это полезным, то здесь (без каких-либо гарантий!) [/ g3]
Это немного базовый но, похоже, это делает работу. Очевидно, что он более устарел от более поздних версий Java и других языков JVM, которые поддерживают безопасную навигацию или Optional
.
Предполагая, что структура класса действительно не под нашим контролем, как кажется, имеет место, я думаю, что уловка NPE, предложенная в вопросе, действительно разумное решение, если только производительность не является серьезной проблемой. Одним из небольших улучшений может быть обход логики throw / catch, чтобы избежать беспорядка:
static <T> T get(Supplier<T> supplier, T defaultValue) {
try {
return supplier.get();
} catch (NullPointerException e) {
return defaultValue;
}
}
Теперь вы можете просто сделать:
return get(() -> wsObject.getFoo().getBar().getBaz().getInt(), -1);
Стоит подумать о создании собственного Исключения. Назовем это MyOperationFailedException. Вы можете бросить его, вместо этого вернув значение. Результат будет таким же - вы выйдете из функции, но вы не вернете жестко заданное значение -1, которое является анти-шаблоном Java. В Java мы используем Исключения.
try {
return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
throw new MyOperationFailedException();
}
EDIT:
Согласно обсуждению в комментариях, позвольте мне добавить что-то к моим предыдущим мыслям. В этом коде есть две возможности. Один из них заключается в том, что вы принимаете значение null, а другое - то, что это ошибка.
Если это ошибка, и это происходит, вы можете отлаживать свой код с помощью других структур для целей отладки, когда контрольных точек недостаточно .
Если это приемлемо, вам не важно, где этот нуль появился. Если вы это сделаете, вы определенно не должны связывать эти запросы.
Try
вместоOptional
. Хотя в Java API нетTry
, существует множество libs, обеспечивающих один, например. javaslang.io , github.com/bradleyscollins/try4j , functionaljava.org или github.com/jasongoodwin/better-java- монады – Landei 22 June 2016 в 10:21FClass::getBar
и т. д. будет короче. – Boris the Spider 22 June 2016 в 10:50static
, который повлечет за собой очень незначительное наказание. – Boris the Spider 22 June 2016 в 10:54