Getter на вложенных объектах без NullPointerException [duplicate]

Нет ни одного правильного порядка. Выберите систему и придерживайтесь ее. Я использую:

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"
100
задан frank 21 July 2016 в 21:27
поделиться

17 ответов

Ловить 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-тесты могут быть лучше.

123
ответ дан Lii 20 August 2018 в 12:34
поделиться
  • 1
    Очень хороший ответ. Следует отметить, что если есть другие возможные условия отказа, и вам нужно различать их, вы можете использовать Try вместо Optional. Хотя в Java API нет Try, существует множество libs, обеспечивающих один, например. javaslang.io , github.com/bradleyscollins/try4j , functionaljava.org или github.com/jasongoodwin/better-java- монады – Landei 22 June 2016 в 10:21
  • 2
    FClass::getBar и т. д. будет короче. – Boris the Spider 22 June 2016 в 10:50
  • 3
    @BoristheSpider: Может быть, немного. Но я обычно предпочитаю lambdas методам refs, потому что часто имена классов намного дольше, и я нахожу lambdas немного легче читать. – Lii 22 June 2016 в 10:53
  • 4
    @Lii достаточно справедливо, но обратите внимание, что ссылка на метод может быть немного быстрее, поскольку лямбда может потребовать более сложные компиляции времени. Лямбда потребует генерации метода static, который повлечет за собой очень незначительное наказание. – Boris the Spider 22 June 2016 в 10:54
  • 5
    @Lii Я действительно нахожу ссылки на методы более чистыми и более наглядными, даже если они немного длиннее. – shmosel 23 June 2016 в 02:17

Предлагаю рассмотреть 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-парсер, который вы используете.

11
ответ дан Andrew Tobilko 20 August 2018 в 12:34
поделиться
  • 1
    Objects.requireNonNull в итоге выбрасывает NullPointerException. Таким образом, это не делает ситуацию иной, чем return wsObject.getFoo().getBar().getBaz().getInt() – Arka Ghosh 22 June 2016 в 07:18
  • 2
    @ArkaGhosh, также он избегает большого количества if s, поскольку OP показал – Andrew Tobilko 22 June 2016 в 07:25
  • 3
    Это единственное разумное решение. Все остальные советуют использовать исключения для управления потоком, которые являются запахами кода. На стороне примечание: я считаю, что метод, связанный с OP, тоже пахнет. Если он будет работать с тремя локальными переменными, и соответствующая if, ситуация будет намного яснее. Также я думаю, что проблема глубже, чем просто работать с NPE: OP должен спросить себя, почему геттеры могут возвращать null. Что означает значение null? Может быть, какой-то нуль-объект будет лучше? Или сбой в одном геттере со значимым исключением? В основном все лучше, чем исключения для управления потоком. – Marius K. 22 June 2016 в 07:38
  • 4
    Безусловный совет использовать исключения, чтобы сигнализировать об отсутствии действительного возвращаемого значения, не очень хорош. Исключения полезны, когда метод терпит неудачу способом, который трудно для вызывающего пользователя восстановить и который лучше обрабатывается в try-catch-statement в какой-либо другой части программы. Чтобы просто сигнализировать об отсутствии возвращаемого значения, лучше использовать класс 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.

4
ответ дан Arka Ghosh 20 August 2018 в 12:34
поделиться

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

Вам придется поймать исключение, где бы вы ни называли метод (или он будет распространяться по стеку). Тем не менее, если в вашем случае вы можете продолжать работать с этим результатом со значением -1, и вы уверены, что он не будет распространяться, потому что вы не используете ни одну из «частей», которая может быть нулевой, тогда мне кажется правильным

Изменить:

Я согласен с более поздним ответом из @xenteros, лучше запустить собственное исключение, вместо этого возвращая -1, вы можете назовите его InvalidXMLException, например.

5
ответ дан Community 20 August 2018 в 12:34
поделиться
  • 1
    Как этот ответ имеет 2 upvotes? – Marius K. 22 June 2016 в 08:07
  • 2
    Это спорно, если это disobeyes Закон Деметры, поскольку WsObject вероятно только структура данных. См. Здесь: stackoverflow.com/a/26021695/1528880 – DerM 22 June 2016 в 09:22
  • 3
    @DerM Да, это возможно, но поскольку OP уже имеет что-то, что анализирует его XML-файл, он также может подумать о создании подходящих классов моделей для требуемых тегов, поэтому библиотека синтаксического анализа может отображать их. Затем эти классы моделей содержат логику проверки null своих собственных подтеков. – Tom 22 June 2016 в 09:45
  • 4
    Что вы имеете в виду, «неважно, поймаете ли вы, он может распространяться на другие части кода»? – Hulk 22 June 2016 в 12:20
  • 5
    Если нуль в этом предложении wsObject.getFoo () И в более поздних частях кода вы снова запускаете этот запрос или используете wsObject.getFoo (). GetBar () (например) снова поднимет исключение NullPointerException. – SCouto 22 June 2016 в 13:21
  • 6
    Это необычная формулировка для «вам придется поймать исключение, где бы вы ни хотели вызвать метод (или он будет распространяться по стеку)». если я правильно понимаю. Я согласен с этим (и это может быть проблемой), я просто считаю эту формулировку путаной. – Hulk 23 June 2016 в 05:18
  • 7
    Я исправлю это, извините, английский не мой первый язык, так что иногда это может случиться :) Спасибо – SCouto 23 June 2016 в 07:10

Я хотел бы добавить ответ, в котором основное внимание будет уделено значению ошибки. Исключительное исключение само по себе не дает никакого значения полной ошибки. Поэтому я бы посоветовал не обращаться к ним напрямую.

Существует тысячи случаев, когда ваш код может пойти не так: невозможно подключиться к базе данных, исключению 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"
    }
}
2
ответ дан Hoàng Long 20 August 2018 в 12:34
поделиться
  • 1
    Непроверенные исключения указывают на то, что программист неправильно использует API. Внешние проблемы, такие как «не могут подключаться к базе данных, исключение IO, сетевая ошибка», должны быть указаны проверенными исключениями. – Kevin Krumwiede 23 June 2016 в 06:56
  • 2
    Это действительно зависит от потребности вызывающего. Исправлена ​​ошибка справки, поскольку она заставляла вас обрабатывать ошибку. Однако в других случаях это не обязательно и может загрязнять код. Например, у вас есть IOException в вашем слое данных, вы бросите его на уровень презентации? Это означало бы, что вы должны поймать исключение и повторно бросить на каждого абонента. Я бы предпочел обернуть IOException с помощью специального бизнес-исключения с соответствующим сообщением и позволить ему выскочить через стек, пока глобальный фильтр не поймает его и не отобразит сообщение пользователю. – Hoàng Long 23 June 2016 в 09:52
  • 3
    Абонентам не нужно ловить и перебрасывать проверенные исключения, просто объявляйте их брошенными. – Kevin Krumwiede 23 June 2016 в 10:28
  • 4
    @KevinKrumwiede: вы правы, нам нужно объявить исключение. Однако нам все равно нужно объявить. Редактирование: сделав второй взгляд на это, есть довольно много дебатов об проверенных vs непроверенных случаях использования исключений (например: programers.stackexchange.com/questions/121328/… ). – Hoàng Long 24 June 2016 в 01:50

Предоставление ответа, который кажется отличным от всех остальных.

Я рекомендую вам проверить NULL в if s.

Причина:

Мы не должны оставлять шанс, чтобы наша программа была разбита. NullPointer генерируется системой. Поведение генерируемых системой исключений невозможно предсказать. Вы не должны оставлять свою программу в руках Системы, когда у вас уже есть способ ее обработки самостоятельно. И добавьте механизм обработки исключений для дополнительной безопасности. !!

Для упрощения чтения кода проверьте это для проверки условий:

if (wsObject.getFoo() == null || wsObject.getFoo().getBar() == null || wsObject.getFoo().getBar().getBaz() == null) 
   return -1;
else 
   return wsObject.getFoo().getBar().getBaz().getInt();

EDIT:

Здесь вам нужно сохранить эти значения wsObject.getFoo(), wsObject.getFoo().getBar(), wsObject.getFoo().getBar().getBaz() в некоторых переменных. Я не делаю этого, потому что я не знаю возвращаемых типов этих функций.

Любые предложения будут оценены .. !!

0
ответ дан Janki Gadhiya 20 August 2018 в 12:34
поделиться
  • 1
    Вы считали getFoo () очень временную операцию? Вы должны хранить возвращаемые значения в переменных, но это пустая трата памяти. Ваш метод идеально подходит для программирования на C. – xenteros 22 June 2016 в 07:23
  • 2
    но когда-нибудь лучше быть 1 миллисекундой позже, тогда программа будет получать сбои @xenteros .. !! – Janki Gadhiya 22 June 2016 в 07:28
  • 3
    getFoo () может получить значение с сервера, расположенного на другом континенте. Он может длиться в любое время: мин / час ... – xenteros 22 June 2016 в 07:29
  • 4
    wsObject будет содержать значение, возвращаемое из Webservice .. !! Служба будет вызвана уже, и wsObject получит длинные данные XML как ответ webservice .. !! Таким образом, нет ничего похожего на сервер , расположенный на другом континенте , потому что getFoo() - это просто элемент, получающий метод getter , а не вызов Webservice .. !! @xenteros – Janki Gadhiya 22 June 2016 в 07:33
  • 5
    Ну, из названий геттеров я бы предположил, что они возвращают объекты Foo, Bar и Baz: P Также рассмотрите возможность удаления упомянутой двойной безопасности из вашего ответа. Я не думаю, что это дает реальную ценность в сторону загрязнения кода. При использовании правильных локальных переменных и нулевой проверки мы сделали более чем достаточно, чтобы обеспечить правильность кода. Если может возникнуть исключение, оно должно рассматриваться как одно. – Marius K. 22 June 2016 в 07:57

Если эффективность является проблемой, следует рассмотреть вариант «улов». Если «catch» не может использоваться, потому что он будет распространяться (как упоминается «SCouto»), используйте локальные переменные, чтобы избежать множественных вызовов методов getFoo(), getBar() и getBaz().

1
ответ дан JEP 20 August 2018 в 12:34
поделиться

Чтобы улучшить читаемость, вы можете использовать несколько переменных, например

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();
3
ответ дан JimmyB 20 August 2018 в 12:34
поделиться
  • 1
    На мой взгляд, это гораздо менее читаемо. Он помещает метод с целым пучком логики проверки нуля, который совершенно не имеет отношения к реальной логике метода. – TroyS 18 October 2016 в 23:33

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

2
ответ дан Kevin Krumwiede 20 August 2018 в 12:34
поделиться
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;
    }
}
1
ответ дан Khaled.K 20 August 2018 в 12:34
поделиться

Если вы не хотите реорганизовывать код, и вы можете использовать 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 ​​

Интерфейс Getter - это просто функциональный интерфейс, вы можете использовать любой эквивалент. GetterResult класс, аксессоры, выделенные для ясности, удерживают результат геттерной цепи, если таковой имеется, или индекс последнего геттера.

Метод getterChain - это простая, шаблонная часть кода, которая может быть сгенерирована автоматически (или вручную при необходимости). Я структурировал код так, чтобы повторяющийся блок был само собой разумеющимся.


Это не идеальное решение, так как вам по-прежнему необходимо определить одну перегрузку getterChain на количество геттеров.

Я бы реорганизовал код вместо этого, но если не удается, и вы часто используете свои длинные геттерные цепи, вы можете подумать о создании класса с перегрузками, которые берут от 2 до, скажем, 10, getters.

2
ответ дан Margaret Bloom 20 August 2018 в 12:34
поделиться

Следили за этим сообщением со вчерашнего дня.

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

2
ответ дан NewUser 20 August 2018 в 12:34
поделиться
  • 1
    Три дополнительных секунды на миллион запросов в худшем случае являются измеримыми, но редко бывают прерываниями транзакций, даже в «критически важных приложениях». Существуют системы, в которых добавление 3,2 микросекунд в запрос является большой проблемой, и если у вас есть такая система, обязательно тщательно обдумайте исключения. Но вызов веб-сервиса и десериализация его вывода по первому вопросу, вероятно, занимает гораздо больше времени, и беспокоиться о производительности обработки исключений не так. – Jeroen Mostert 23 June 2016 в 13:08
  • 2
    @JeroenMostert: 3 секунды за проверку / Миллион. Таким образом, количество проверок увеличит стоимость – NewUser 23 June 2016 в 13:10
  • 3
    Правда. Даже при этом я все же рассматриваю это случай «профиля в первую очередь», хотя. Вам понадобится более 300 проверок в одном запросе, прежде чем запрос займет полную миллисекунду. Конструктивные соображения будут весить на мою душу гораздо раньше, чем это. – Jeroen Mostert 23 June 2016 в 13:13
  • 4
    @JeroenMostert: :) Согласен! Я хотел бы оставить его программисту с результатом и позволить им позвонить! – NewUser 23 June 2016 в 13:18

Метод, который у вас есть, длинный, но очень читаемый. Если бы я был новым разработчиком, который приходил на вашу базу кода, я мог бы видеть, что вы делаете довольно быстро. На мой взгляд, большинство других ответов (в том числе и для исключения исключения) не делают вещи более читабельными, а некоторые делают их менее читаемыми.

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

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();
    }

}
1
ответ дан Pace 20 August 2018 в 12:34
поделиться

Я написал класс под названием 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.

0
ответ дан Rich 20 August 2018 в 12:34
поделиться

Предполагая, что структура класса действительно не под нашим контролем, как кажется, имеет место, я думаю, что уловка 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);
6
ответ дан shmosel 20 August 2018 в 12:34
поделиться

Стоит подумать о создании собственного Исключения. Назовем это MyOperationFailedException. Вы можете бросить его, вместо этого вернув значение. Результат будет таким же - вы выйдете из функции, но вы не вернете жестко заданное значение -1, которое является анти-шаблоном Java. В Java мы используем Исключения.

try {
    return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
    throw new MyOperationFailedException();
}

EDIT:

Согласно обсуждению в комментариях, позвольте мне добавить что-то к моим предыдущим мыслям. В этом коде есть две возможности. Один из них заключается в том, что вы принимаете значение null, а другое - то, что это ошибка.

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

Если это приемлемо, вам не важно, где этот нуль появился. Если вы это сделаете, вы определенно не должны связывать эти запросы.

1
ответ дан xenteros 20 August 2018 в 12:34
поделиться
  • 1
    Разве вы не думаете, что это плохая идея, чтобы подавить исключение? В реальном времени, если мы потеряем след исключения, его реальная боль внизу, чтобы узнать, что, черт возьми, происходит! Я всегда предлагаю не использовать цепочку. Вторая проблема, которую я вижу, заключается в следующем: этот код не может назначаться в момент времени, который из результата был нулевым. – NewUser 22 June 2016 в 07:30
  • 2
    Нет, ваше исключение может иметь сообщение, которое, несомненно, укажет на то место, где оно было выброшено. Я согласен, что цепочка не лучшее решение :) – xenteros 22 June 2016 в 07:31
  • 3
    Нет, он просто сказал бы о номере линии. Таким образом, любое из вызовов в цепочке может привести к исключению. – NewUser 22 June 2016 в 07:32
  • 4
    проверить мое обновление – xenteros 22 June 2016 в 07:45
  • 5
    & quot; Если это ошибка, и это происходит, вы можете отлаживать свой код & quot; - не в производстве. Я бы предпочел бы знать, что произошло, когда все, что у меня есть, это журнал, чем пытаться объяснить, что случилось, чтобы заставить его потерпеть неудачу. С этим советом (и этим кодом) все, что вы действительно знаете, это то, что одна из четырех вещей была нулевой, но не какой-либо или почему. – vlaz 22 June 2016 в 21:27
5
ответ дан Community 31 October 2018 в 10:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: