Wie führe ich Logik auf Optional aus, wenn sie nicht vorhanden ist?


83

Ich möchte den folgenden Code mit java8 ersetzen Optional:

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

    return obj;
}

Der folgende Pseudocode funktioniert nicht, da es keine orElseRunMethode gibt, aber er veranschaulicht trotzdem meinen Zweck:

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

Was möchten Sie von der Methode zurückgeben, wenn kein Objekt vorhanden ist?
Duncan Jones

Ich möchte Optionalimmer zurückkehren, wie durch den Parameter method return angegeben.
Membersound

Antworten:


123

Mit Java 9 oder höher ifPresentOrElseist höchstwahrscheinlich das, was Sie wollen:

Optional<> opt = dao.find();

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

Currying mit vavr oder ähnlichem könnte noch ordentlicheren Code erhalten, aber ich habe es noch nicht versucht.


62
scheint etwas zu sein, das in v1 (Java 8) enthalten sein sollte ...
na ja

2
Ja ... ich denke auch, dass sie das in Java 8 tatsächlich verpasst haben. Und mehr noch ... wenn Sie etwas tun möchten, wenn der Wert vorhanden ist, haben sie "ifPresent ()" angegeben. Wenn Sie etwas tun möchten, wenn der Wert vorhanden ist, und etwas anderes, wenn dies nicht der Fall ist, haben sie "ifPresentOrElse (f1, f2)" angegeben. Aber es fehlt immer noch, wenn ich nur etwas damit machen möchte, dass es nicht vorhanden ist (so etwas wie "ifNotPresent ()" wäre geeignet). Mit ifPresentOrElse bin ich gezwungen, eine Present-Funktion zu verwenden, die im späteren Fall nichts bewirkt.
Hbobenicio

Wenn Sie ein Framework einführen können, werfen Sie einen Blick auf Vavr (ehemals Javaslang) und dessen Option, es hat eine onEmpty-Methode
Andreas

Verwenden Sie lieber Java 9 oder if, else. vavr ist nicht sehr nett
senseiwu

Dies ist übermäßig komplex, nur für eine Straße, wenn dann sonst!
JBarros35

36

Ich glaube nicht, dass Sie es in einer einzigen Aussage schaffen können. Besser machen:

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

37
Dies mag die richtige Antwort sein, aber inwiefern ist dies nullSchecks überlegen ? Aus meiner Sicht ist es schlimmer ohne orElse....
DaRich

4
@DaRich, Sie können die Null in der Mitte Ihres Codes vergessen, was zu der NPE führt. Aber Sie können eine nicht versehentlich ignorieren Optional, es ist immer eine explizite (und gefährliche) Entscheidung.
Dherik

16

Für Java 8 bietet Spring ifPresentOrElse"Utility-Methoden zum Arbeiten mit Optionals" an, um das zu erreichen, was Sie wollen. Beispiel wäre:

import static org.springframework.data.util.Optionals.ifPresentOrElse;    

ifPresentOrElse(dao.find(), obj -> obj.setAvailable(true), () -> logger.fatal("Object not available"));

11

Sie müssen dies in mehrere Anweisungen aufteilen. Hier ist eine Möglichkeit, dies zu tun:

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

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

Ein anderer (möglicherweise überentwickelter) Weg ist die Verwendung von map:

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

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

Wenn Sie obj.setAvailablebequem zurückkehren obj, können Sie einfach das zweite Beispiel zu:

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

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

9

Zuallererst dao.find()sollten Sie entweder eine zurückgeben Optional<Obj>oder Sie müssen eine erstellen.

z.B

Optional<Obj> = dao.find();

oder du kannst es selbst machen wie:

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

Dieser wird zurückkehren, Optional<Obj>wenn vorhanden oder Optional.empty()nicht vorhanden.

Kommen wir nun zur Lösung:

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

Dies ist der Liner, den Sie suchen :)


9
Die Rückgabe einer Null macht den Zweck von Optionals zunichte. In der OP-Frage ist nicht eindeutig, was zu tun ist, wenn das Objekt nicht gefunden wird. IMHO ist es besser, ein neu instanziiertes Objekt zurückzugeben und möglicherweise auf false zu setzen. Natürlich hat sich das OP hier als tödlich gemeldet, was bedeutet, dass er wahrscheinlich beabsichtigt, zu kündigen, so dass es nicht wirklich wichtig ist.
Somaiah Kumbera

Diese Lösung gibt ein zurück Object, während die ursprüngliche Frage eine Methode betrifft, die zurückgibt Optional<Object>. Meine (ältere) Antwort ist sehr ähnlich, unterscheidet sich jedoch in dieser Hinsicht: stackoverflow.com/a/36681079/3854962
UTF_or_Death

Warum die Verwendung von flatMap?
Lino

weil er ein optionales anstelle eines Wertes zurückgibt. FlatMap konvertiert Optional <Optional <X >> in Optional <X>
Aman Garg

9

Es gibt eine .orElseRunMethode, die aber aufgerufen wird .orElseGet.

Das Hauptproblem mit Ihrem Pseudocode ist, dass .isPresentkein zurückgegeben wird Optional<>. Aber .mapgibt eine , Optional<>die das hat orElseRunMethode.

Wenn Sie dies wirklich in einer Anweisung tun möchten, ist dies möglich:

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

Aber das ist noch klobiger als das, was Sie vorher hatten.


3

Ich konnte ein paar "One Line" -Lösungen entwickeln, zum Beispiel:

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

oder

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

oder

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

Es sieht nicht sehr gut aus, so etwas orElseRunwäre viel besser, aber ich denke, diese Option mit Runnable ist akzeptabel, wenn Sie wirklich eine einzeilige Lösung wollen.


1

Mit Java 8 Optionalist Folgendes möglich:

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

    obj.map(obj.setAvailable(true)).orElseGet(() -> {
        logger.fatal("Object not available");
        return null;
    });

1

Für diejenigen unter Ihnen, die einen Nebeneffekt nur ausführen möchten, wenn ein optionales fehlt

dh ein Äquivalent von ifAbsent()oder ifNotPresent()hier ist eine geringfügige Änderung der bereits gegebenen großartigen Antworten.

myOptional.ifPresentOrElse(x -> {}, () -> {
  // logic goes here
})

1
ifPresentOrElse benötigt Java 9.
JL_SO


0

ifPresentOrElse kann auch Fälle von Nullzeigern behandeln. Einfacher Ansatz.

   Optional.ofNullable(null)
            .ifPresentOrElse(name -> System.out.println("my name is "+ name),
                    ()->System.out.println("no name or was a null pointer"));

-2

Ich nehme an, Sie können die dao.find()Methode zur Rückgabe einer Instanz von nicht ändern Optional<Obj>, daher müssen Sie die entsprechende Methode selbst erstellen.

Der folgende Code soll Ihnen helfen. Ich habe die Klasse erstellt OptionalAction, die den If-else-Mechanismus für Sie bereitstellt.

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

1
Bitte hinterlassen Sie einen Kommentar, wenn Sie abstimmen. Dies hilft mir, die Antwort zu verbessern.
Mike

Ich bin damit einverstanden, dass Downvoter hier einen Kommentar abgeben sollte. Ich gehe davon aus, dass ich Java7 in Java8-Code umgestalten wollte, während der alte Code aus 8 Zeilen bestand. Und wenn ich es durch Ihren Vorschlag ersetzen würde , würde das niemandem helfen, sondern die Dinge nur noch schlimmer machen.
Membersound

Ich verstehe deinen Standpunkt nicht. Ich habe Java 7 bis 8 umgestaltet, nicht wahr? Und inwieweit würde es die Dinge noch schlimmer machen? Ich sehe keine Mängel in dieser Lösung. Man könnte argumentieren, ob der Sinn einer anderen (oder einer Problemumgehung) OptionalSinn macht. Aber ich habe Ihre Frage richtig beantwortet und ein funktionierendes Beispiel geliefert.
Mike

Ihre Lösung scheint mir völlig gültig zu sein, Mike. Auf OptionalActionjeden Fall scheint die Einführung expliziter Klassen wie eine Problemumgehung für die Portierung des Codes auf Java8 etwas überarbeitet zu sein, wenn dies in Java7 bereits nur ein paar Zeilen sind.
Membersound

2
Optionales <DbObject> object = (v == null)? Optional.leer (): Optional.von (v); kann umgeschrieben werden in: Optional <DbObject> object = Optional.ofNullable (v);
Geir
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.