2015-03-24 16 views
38

Chcę zastąpić następujący kod przy użyciu java8 Optional:Jak wykonać logikę na Opcjonalne, jeśli nie występuje?

public Obj getObjectFromDB() { 
    Obj obj = dao.find(); 
    if (obj != null) { 
     obj.setAvailable(true); 
    } else { 
     logger.fatal("Object not available"); 
    } 

    return obj; 
} 

Poniższy pseudokod nie działa, gdyż nie ma orElseRun metoda, ale tak czy inaczej to ilustruje mój cel:

public Optional<Obj> getObjectFromDB() { 
    Optional<Obj> obj = dao.find(); 
    return obj.ifPresent(obj.setAvailable(true)).orElseRun(logger.fatal("Object not available")); 
} 
+0

Czego chcesz powrócić z metody, jeśli nie ma obiektu? –

+0

Chciałbym zwrócić 'Opcjonalnie' zawsze tak, jak wskazuje parametr powrotu metody. – membersound

Odpowiedz

22

I don” Sądzę, że możesz to zrobić w jednym komunikacie. Lepiej zrób:

if (!obj.isPresent()) { 
    logger.fatal(...); 
} else { 
    obj.get().setAvailable(true); 
} 
return obj; 
+22

To może być właściwa odpowiedź, ale w jaki sposób jest ona lepsza od kontroli "zerowej"? Z mojego punktu widzenia jest gorzej bez "orElse ...". – DaRich

7

Będziesz musiał podzielić to na wiele instrukcji. Oto jeden ze sposobów, aby to zrobić:

if (!obj.isPresent()) { 
    logger.fatal("Object not available"); 
} 

obj.ifPresent(o -> o.setAvailable(true)); 
return obj; 

Innym sposobem (prawdopodobnie ponad inżynierii) jest użycie map:

if (!obj.isPresent()) { 
    logger.fatal("Object not available"); 
} 

return obj.map(o -> {o.setAvailable(true); return o;}); 

Jeśli obj.setAvailable dogodnie zwraca obj, wtedy można po prostu drugi przykład do:

if (!obj.isPresent()) { 
    logger.fatal("Object not available"); 
} 

return obj.map(o -> o.setAvailable(true)); 
+0

^1 - AFAIK, to jest najlepsza odpowiedź tutaj. –

-1

Przypuszczam, że nie można zmienić metodę dao.find() powrócić instancję Optional<Obj>, więc musisz sam utworzyć odpowiedni.

Poniższy kod powinien ci pomóc. Stworzyłem klasę OptionalAction, , która zapewnia mechanizm if-else dla ciebie.

public class OptionalTest 
{ 
    public static Optional<DbObject> getObjectFromDb() 
    { 
    // doa.find() 
    DbObject v = find(); 

    // create appropriate Optional 
    Optional<DbObject> object = Optional.ofNullable(v); 

    // @formatter:off 
    OptionalAction. 
    ifPresent(object) 
    .then(o -> o.setAvailable(true)) 
    .elseDo(o -> System.out.println("Fatal! Object not available!")); 
    // @formatter:on 
    return object; 
    } 

    public static void main(String[] args) 
    { 
    Optional<DbObject> object = getObjectFromDb(); 
    if (object.isPresent()) 
     System.out.println(object.get()); 
    else 
     System.out.println("There is no object!"); 
    } 

    // find may return null 
    public static DbObject find() 
    { 
    return (Math.random() > 0.5) ? null : new DbObject(); 
    } 

    static class DbObject 
    { 
    private boolean available = false; 

    public boolean isAvailable() 
    { 
     return available; 
    } 

    public void setAvailable(boolean available) 
    { 
     this.available = available; 
    } 

    @Override 
    public String toString() 
    { 
     return "DbObject [available=" + available + "]"; 
    } 
    } 

    static class OptionalAction 
    { 
    public static <T> IfAction<T> ifPresent(Optional<T> optional) 
    { 
     return new IfAction<>(optional); 
    } 

    private static class IfAction<T> 
    { 
     private final Optional<T> optional; 

     public IfAction(Optional<T> optional) 
     { 
     this.optional = optional; 
     } 

     public ElseAction<T> then(Consumer<? super T> consumer) 
     { 
     if (optional.isPresent()) 
      consumer.accept(optional.get()); 
     return new ElseAction<>(optional); 
     } 
    } 

    private static class ElseAction<T> 
    { 
     private final Optional<T> optional; 

     public ElseAction(Optional<T> optional) 
     { 
     this.optional = optional; 
     } 

     public void elseDo(Consumer<? super T> consumer) 
     { 
     if (!optional.isPresent()) 
      consumer.accept(null); 
     } 
    } 
    } 
} 
+0

Proszę zostaw komentarz, jeśli zagłosujesz. Pomaga mi to poprawić odpowiedź. – mike

+0

Zgadzam się, że downvoter powinien komentować tutaj. Zakładam, że dzieje się tak dlatego, że chciałem zmienić kod java7 na java8, podczas gdy stary kod składał się z 8 linii. A jeśli * * zastąpię to twoją sugestią, która nikomu nie pomoże, a tylko pogorszy sprawę. – membersound

+0

Nie rozumiem twojego punktu widzenia. Zrobiłem refaktor Java 7 do 8, prawda? I w jakim stopniu pogorszyłoby to sytuację? Nie widzę żadnych wad tego rozwiązania. Można argumentować, czy cały sens posiadania czegoś innego (lub rozwiązania zastępczego) w opcji "Opcjonalnie" ma sens. Ale poprawnie odpowiedziałem na twoje pytanie i przedstawiłem przykład. – mike

0

byłem w stanie wymyślił kilka rozwiązań "pierwsza linia", na przykład:

obj.map(o -> (Runnable)() -> o.setAvailable(true)) 
     .orElse(() -> logger.fatal("Object not available")) 
     .run(); 

lub

obj.map(o -> (Consumer<Object>) c -> o.setAvailable(true)) 
     .orElse(o -> logger.fatal("Object not available")) 
     .accept(null); 

lub

obj.map(o -> (Supplier<Object>)() -> { 
      o.setAvailable(true); 
      return null; 
    }).orElse(()() -> { 
      logger.fatal("Object not available") 
      return null; 
    }).get(); 

It nie wygląda ładnie, coś w stylu orElseRun byłby znacznie lepszy, ale myślę, że opcja z Runnable jest akceptowalna, jeśli naprawdę chcesz jedno rozwiązanie liniowe.

2

Nie jest metoda .orElseRun, ale to się nazywa .orElseGet, problemem jest to, że w przeciwieństwie .map, .isPresent nie zwraca Optional<Obj>.

Jeśli naprawdę chcesz to zrobić w jednej instrukcji jest to możliwe:

public Obj getObjectFromDB() { 
    return dao.find() 
     .map(obj -> { 
      obj.setAvailable(true); 
      return Optional.of(obj); 
     }) 
     .orElseGet(() -> { 
      logger.fatal("Object not available"); 
      return Optional.empty(); 
    }); 
} 

Ale to nawet clunkier niż to, co przedtem.

9

Po pierwsze, Twój dao.find() powinien albo zwrócić wartość Optional<Obj>, albo trzeba ją utworzyć.

np.

Optional<Obj> = dao.find(); 

lub możesz zrobić to sam lubię:

Optional<Obj> = Optional.ofNullable(dao.find()); 

ten powróci Optional<Obj> jeśli obecna lub Optional.empty() jeśli nie występuje.

Więc teraz przejdźmy do roztworu

public Obj getObjectFromDB() { 
    return Optional.ofNullable(dao.find()).flatMap(ob -> { 
      ob.setAvailable(true); 
      return Optional.of(ob);  
     }).orElseGet(() -> { 
      logger.fatal("Object not available"); 
      return null; 
     }); 
    } 

Jest to jeden liner szukasz :)

+6

Zwrócenie wartości zerowej powoduje przekroczenie celu opcji. W pytaniu OP, to, co trzeba zrobić, jeśli obiekt nie zostanie znaleziony, jest niejednoznaczne. IMHO lepiej jest zwrócić nowo utworzony obiekt, a być może ustawić opcję Dowolny na false. Oczywiście w tym przypadku PO odnotował śmierć, co oznacza, że ​​prawdopodobnie zamierza ją zakończyć, więc nie ma to znaczenia. –

+0

To rozwiązanie zwraca 'Object', podczas gdy oryginalne pytanie dotyczy metody zwracającej' Opcjonalnie '. Moja (starsza) odpowiedź jest bardzo podobna, ale różni się w ten sposób: http://stackoverflow.com/a/36681079/3854962 –

41

Java 9 nowego ifPresentOrElse jest najprawdopodobniej to, co chcesz:

Optional<> opt = dao.find(); 

opt.ifPresentOrElse(obj -> obj.setAvailable(true), 
        () -> logger.error("…")); 

Currying przy użyciu javaslang lub podobnie może uzyskać jeszcze lepszy kod, ale jeszcze nie próbowałem.

+8

wydaje się być czymś, co powinno być zawarte w v1 (Java 8) ... _oh cóż ... _ – ycomp

+1

Tak ...Sądzę też, że faktycznie ominęli to w Javie 8. I więcej ... jeśli chcesz coś zrobić, gdy wartość jest obecna, podali "ifPresent()". Jeśli chcesz coś zrobić, gdy wartość jest obecna, a inne, gdy nie jest, podały "ifPresentOrElse (f1, f2)". Ale nadal brakuje, jeśli chcę tylko coś z tym zrobić, nie jest obecny (coś w stylu "ifNotPresent()" byłoby odpowiednie). Przy pomocy ifPresentOrElse zmuszony jestem użyć funkcji present, która nie robi nic w późniejszym przypadku. – hbobenicio

+0

Jeśli możesz wprowadzić framework, spójrz na Vavr (dawny JavaScript) i ich Opcję, ma on [metodę onEmpty] (https://static.javadoc.io/io.vavr/vavr/0.9.0/io /vavr/control/Option.html#onEmpty-java.lang.Runnable-) – Andreas