Как обработать регистрацию нулевых значений в дополнительной цепочке Java - PullRequest
2 голосов
/ 30 апреля 2019

Скажем, я использую дополнительные функции, и у меня есть:

A a;
....
Optional.ofNullable(a)
    .map(A::getB)
    .map(B::getC)
    .ifPresent(c -> ...) // do something with c

Однако, если по какой-либо причине я хочу войти, когда B :: getC равен нулю (или когда a равен нулю). Есть ли какая-то идиома для борьбы с этим? Я мог бы сделать несколько вложенных вызовов ifPresent, но это очень многословно.

Лучшее, что я мог придумать, это класс FunctionalHelper, который оборачивает некоторые дополнительные методы и добавляет другие для поддержки ведения журнала (log4j):

private static final Logger LOG = //...
private static final FunctionalHelper FH 
    = new FunctionalHelper(LOG, Level.DEBUG);

FH.ofNullable(a, Level.WARN, "You gave me a null A"))
    .map(FH.logNull(A::getB, "b was null for a: {}", a-> FH.args(a.getName()))
    .map(B::getC)
    .ifPresent(c -> ...) // do something with c

Это работает, но немного похоже на решение проблемы. Есть ли какая-то идиома (или, по крайней мере, стандартная библиотека) для работы с такими вещами?

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

ОБНОВЛЕНИЕ: В ответ на @Ole V.V., вот пример с Optionals (ОБНОВЛЕНИЕ2: я немного подправил его, чтобы соответствовать семантике поставщика log4j varags, но с функциями:

private static final FunctionalHelper FH  = new FunctionalHelper(LOG, Level.DEBUG);

void foo(A someA) {
    FH.ofNullable(someA, Level.WARN, "You gave me a null A")
            .map(FH.logNull(A::getB, "B was null for A: {}", A::getName))
            .map(FH.logNull(B::getC, "C was null for B: {}", B::getName))
            .ifPresent(c -> { /* do something with c */});
}

Вот реализация If-else:

void foo2(A a) {
    if (a == null) LOG.debug("You gave me a null A");
    else {
        B b = a.getB();
        if (b == null) LOG.debug("B was null for a: {}", a::getName);
        else {
            String c = b.getC();
            if (c == null) LOG.debug("C was null for a: {}", b::getName);
            else { /* do something with c */ }
        }
    }
}

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

Ответы [ 2 ]

1 голос
/ 30 апреля 2019

Вы можете использовать Optional.orElseGet() вместе с Supplier<A>, который возвращает какое-то значение по умолчанию A (не уверен, что я правильно указал ваши типы).

Supplier<A> nonNullASupplier = () -> {
    LOGGER.log("Invoking supplier because A was null");
    return new A();
}

Тогда в коде вызова:

Optional.ofNullable(a)
    .orElseGet(nonNullASupplier)
    .map(A::getB)
    .map(B::getC)
    .ifPresent(c -> ...) // do something with c

Опять же, не уверен на 100%, какие типы A и B здесь, но это основная идея.

0 голосов
/ 30 апреля 2019

Я не знаю, подойдет ли вам это, но, возможно, вы можете использовать шаблон пустых объектов.

class NullA extends A {
  B getB() {
     return new NullB();
  }
}
class NullB extends B {
  C getC() {
     return new NullC();
  }
}
class NullC extends C {
}

И затем с помощью orElseGet вернуть нулевой объект:

Optional.ofNullable(a)
    .orElseGet(new NullA())
    .map(A::getB)
    .map(B::getC)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...