Проверьте цепочки "получить" звонки на ноль - PullRequest
29 голосов
/ 11 августа 2010

Допустим, я хотел бы выполнить следующую команду:

house.getFloor(0).getWall(WEST).getDoor().getDoorknob();

Чтобы избежать исключения NullPointerException, я должен был бы сделать следующее, если:

if (house != null && house.getFloor(0) && house.getFloor(0).getWall(WEST) != null
  && house.getFloor(0).getWall(WEST).getDoor() != null) ...

Есть лиспособ или уже существующий класс Utils, который делает это более элегантно, скажем, что-то вроде следующего?

checkForNull(house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

Ответы [ 9 ]

34 голосов
/ 14 декабря 2016

В случае, если вы не можете избегать нарушения Закона Деметры (LoD), как указано в выбранном ответе, а при Java 8 вводите Необязательно , это было бы, вероятно, лучшей практикой для обработки нулей в цепочках получения, таких как ваша.

Тип Optional позволит вам передавать несколько операций с картой (которые содержат вызовы get) подряд. Нулевые чеки автоматически обрабатываются под капотом.

Например, когда объекты не инициализированы, print () не будет выполняться, и исключения не будут выброшены. Все это мы бережно берем под капот. Когда объекты будут инициализированы, будет сделана печать.

System.out.println("----- Not Initialized! -----");

Optional.ofNullable(new Outer())
        .map(out -> out.getNested())
        .map(nest -> nest.getInner())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //no print

System.out.println("----- Let's Initialize! -----");

Optional.ofNullable(new OuterInit())
        .map(out -> out.getNestedInit())
        .map(nest -> nest.getInnerInit())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //will print!

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

class OuterInit {
    NestedInit nested = new NestedInit();
    NestedInit getNestedInit() {
        return nested;
    }
}
class NestedInit {
    InnerInit inner = new InnerInit();
    InnerInit getInnerInit() {
        return inner;
    }
}
class InnerInit {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

Итак, с цепочкой ваших геттеров это будет выглядеть так:

Optional.ofNullable(house)
        .map(house -> house.getFloor(0))
        .map(floorZero -> floorZero.getWall(WEST))
        .map(wallWest -> wallWest.getDoor())
        .map(door -> wallWest.getDoor())

Возвращение этого будет что-то вроде Optional<Door>, что позволит вам гораздо безопаснее работать, не беспокоясь о нулевых исключениях.

13 голосов
/ 08 августа 2017

Чтобы проверить цепочку получения для ноль , вам может потребоваться вызвать код из замыкания . Код вызова закрытия будет выглядеть так:

public static <T> T opt(Supplier<T> statement) {       
    try {
        return statement.get();
    } catch (NullPointerException exc) {
        return null;
    }   
}

И вы называете это, используя следующий синтаксис:

Doorknob knob = opt(() -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

Этот код также является типобезопасным и в целом работает как задумано:

  1. Возвращает фактическое значение указанного типа, если все объекты в цепочке не null .
  2. Возвращает ноль , если какой-либо из объектов в цепочке ноль .

Вы можете поместить метод opt в общий класс util и использовать его повсюду в вашем приложении.

13 голосов
/ 11 августа 2010

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

Закон Деметры: http://en.wikipedia.org/wiki/Law_of_Demeter

4 голосов
/ 11 августа 2010

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

if (wall.hasDoor()) {
   wall.getDoor().etc();
}

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

Дело в том, что вы должны что-то делать, если у вас есть null.Например - return или бросить IllegalStateException

А что не следует делать - не ловить NullPointerException.Исключения во время выполнения не предназначены для перехвата - не ожидается, что вы сможете восстановить их, и не рекомендуется полагаться на исключения для логического потока.Представьте, что вы на самом деле не ожидаете, что что-то будет null, и вы ловите (и регистрируете) NullPointerException.Это не будет очень полезной информацией, так как многие вещи могут быть null в этот момент.

4 голосов
/ 11 августа 2010

Конечно, вы можете просто обернуть все выражение в блок try-catch, но это плохая идея. Что-то более чистое - Шаблон нулевого объекта . При этом, если в вашем доме нет этажа 0, он просто возвращает этаж, который действует как обычный этаж, но не имеет реального содержания; Этажи, когда их спрашивают о Стенах, которых у них нет, возвращают аналогичные «Нулевые» Стены и т. Д. По линии.

0 голосов
/ 02 июля 2019

реализуя nullPointer try / catch с поставщиком, вы можете отправить его по всей цепочке получения

public static <T> T getValue(Supplier<T> getFunction, T defaultValue) {
    try {
        return getFunction.get();
    } catch (NullPointerException ex) {
        return defaultValue;
    }
}

и затем назовите это так.

ObjectHelper.getValue(() -> object1.getObject2().getObject3().getObject4()));
0 голосов
/ 17 сентября 2018

Очень старый вопрос, но все же добавляю мое предложение:

Я бы предложил вместо того, чтобы получить дверную ручку из дома, вы должны попытаться предоставить дверную ручку этому классу из вызывающего кода или создатьцентральное средство поиска специально для этой цели (например, сервис DoorKnob)

0 голосов
/ 22 сентября 2013

Вы можете поддержать это в java (просто для добавления синтаксического сахара), используя симиллы techinque к тому, что использовал Mockito:

org.mockito.Mockito.spy(house).getFloor(0).getWall(WEST).getDoor().getDoorknob()

или CatchException

com.googlecode.catchexception.CatchException
    .catchException(house).getFloor(0).getWall(WEST).getDoor().getDoorknob()

Mockito нельзя использовать напрямую, поскольку проверяет ненулевой дом и выдает исключение, в то время как CatchException предназначен для другой цели.

Тем не менее, возможно, кто-то может подсказать, где можно найти такую ​​функциональность.

0 голосов
/ 11 августа 2010

Нет метода checkForNull, который вы можете написать, который бы облегчил это (это просто не то, как вызов метода и оценка аргумента работает в Java).

Вы можете разбить цепочки операторов на несколько операторов,проверка на каждом шагу.Однако, возможно, лучшим решением было бы не возвращать эти методы null.Есть что-то под названием Null Object Pattern , который вы можете использовать вместо этого.

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

...