Null coalescing

0

Cześć,

powiedzmy, że mam funkcję:

public static String getCode(Foo first, Supplier<Foo> second)

interface Foo {

    String getCode();

}

chciałbym, żeby przechodziła test:

expect:
            getCode(new FooImpl(first), { new FooImpl(second) }) == result
where:
            first | second || result
            'A'   | 'B'    || 'A'
            null  | 'B'    || 'B'
            null  | null   || 'default'

Jak byście napisali to w Javie 8? Próbowałem z ifami, ale robi się to mało czytelne i nie podoba mi się.

Optional.or doszedł niestety w Java 9, ale chciałbym uzyskać coś podobnego:

    public static String getCode(Foo first, Supplier<Foo> second) {
        return ofNullable(first.getCode())
                .or(() -> ofNullable(second.get().getCode()))
                .orElse(DEFAULT);
    }

Może znacie jakieś Utilsy (apache, guava), które mają już coś takiego?

3

Zobacz jak wyglada implementacja or() i skopiuj do siebie

1

Wpisałem w kaczkę "java first non null" i takie coś mi wyszło:

http://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/ObjectUtils.html#firstNonNull%28T...%29

A potrzebowałem tego ostatnio...

Edit: dobra, w sumie to nie zadziała, bo tutaj jest Supplier. Jednak w nowszej wersji jest już metoda, która to ogarnia:

http://commons.apache.org/proper/commons-lang/javadocs/api-3.10/org/apache/commons/lang3/ObjectUtils.html#getFirstNonNull-java.util.function.Supplier...-

2

Ja bym tutaj użył instrukcji warunkowej if.

0
Charles_Ray napisał(a):

Ja bym tutaj użył instrukcji warunkowej if.

public static String getCode(Foo first, Supplier<Foo> second) {
        String firstCode = first.getCode();
        if(firstCode != null) {
            return firstCode;
        } else {
            String secondCode = second.get().getCode();
            if(secondCode != null) {
                return secondCode;
            } else {
                return DEFAULT;
            }
        }
    }

Powstaje coś takiego - wydzielenie zawartości else do metody nie pomaga w czytelności. Wolałbym coś bardziej zwięzłego, na co patrząc od razu wiedziałbym co się dzieje. Tutaj nie mam takiego uczucia.

0
    public static String getCode(Foo first, Supplier<Foo> second) {
        return ofNullable(first.getCode())
                .or(() -> ofNullable(second.get().getCode()))
                .orElse(DEFAULT);
    }

można by to zapisać jako:

    public static String getCode(Foo first, Supplier<Foo> second) {
        String result1 = first.getCode();
        String result2 = result1 != null ? result1 : second.get().getCode();
        return result2 != null ? result2 : DEFAULT;
    }

Jeśli result1 != null to second.get().getCode() się nie wykona.

Ewentualnie można jeszcze podejść imperatywnie:

    public static String getCode(Foo first, Supplier<Foo> second) {
        String result = first.getCode();
        if (result == null) { result = second.get().getCode(); }
        if (result == null) { result = DEFAULT; }
        return result;
    }

Albo wracając do Optionali na Javie 8:

    public static String getCode(Foo first, Supplier<Foo> second) {
        return ofNullable(
            ofNullable(firstGetCode())
                .orElseGet(() -> second.get().getCode())
        ).orElse(DEFAULT);
    }

Dużo się nie nawalczy przy tak ubogim Optionalu jak w Javie 8.

0

W kontekście clean coda i wzorców, czy to nie jest temat na chain of responsibility? Trochę armata. Ile tam tych ifow będzie? 2 to jeszcze bez dramatu jak zamknięte w jakiejś klasie z boku

0

Propozycja (niektóre null checks mogą się okazać niepotrzebne):

    public static String getCode(Foo first, Supplier<Foo> second) {
        return Stream.of(() -> first, second)
            .filter(Objects::nonNull)
            .map(Supplier::get)
            .filter(Objects::nonNull)
            .map(Foo::getCode)
            .filter(Objects::nonNull)
            .findFirst()
            .orElse("default");
    }
2

najlepszym rozwiazaniem wydaje mi sie zmiana/opakowanie Foo zeby zwracalo vavr'owe Option<String>.
a jak nie to zrobilabym 2 ify.
mysle ze jak po kodzie lataja bomby w postaci nulli to robienie przeinzynierowanego coalesce jest wymyslaniem sobie problemu zamiast rozwiazywac faktyczny :)

1
mythflame napisał(a):
Charles_Ray napisał(a):

Ja bym tutaj użył instrukcji warunkowej if.

public static String getCode(Foo first, Supplier<Foo> second) {
        String firstCode = first.getCode();
        if(firstCode != null) {
            return firstCode;
        } else {
            String secondCode = second.get().getCode();
            if(secondCode != null) {
                return secondCode;
            } else {
                return DEFAULT;
            }
        }
    }

Powstaje coś takiego - wydzielenie zawartości else do metody nie pomaga w czytelności. Wolałbym coś bardziej zwięzłego, na co patrząc od razu wiedziałbym co się dzieje. Tutaj nie mam takiego uczucia.

public static String getCode(Foo first, Supplier<Foo> second) {
        String firstCode = first.getCode();
        if(firstCode != null) {
            return firstCode;
        }

          String secondCode = second.get().getCode();
          return secondCode == null ? DEFAULT : secondCode;
    }
0
public static String getCode(Foo first, Supplier<Foo> second) {
        String firstCode = first.getCode();
        if(firstCode != null) {
            return firstCode;
        }

          String secondCode = second.get().getCode();
          return secondCode == null ? DEFAULT : secondCode;
    }
  public static String getCode(Foo first, Supplier<Foo> second) {
    return Optional
      .ofNullable(first.getCode())
      .orElseGet(() -> Optional.ofNullable(second.get().getCode()).orElse(DEFAULT));
  }

1 użytkowników online, w tym zalogowanych: 0, gości: 1