存在しない場合、オプションでロジックを実行する方法は?


83

java8を使用して次のコードを置き換えたいOptional

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

    return obj;
}

次の擬似コードはorElseRunメソッドがないため機能しませんが、とにかく私の目的を示しています。

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

オブジェクトが存在しない場合、メソッドから何を返したいですか?
ダンカンジョーンズ

Optionalメソッドのreturnパラメータで示されるように常に戻りたいのですが。
メンバーサウンド2015年

回答:


125

Java 9以降でifPresentOrElseは、おそらくあなたが望むものです:

Optional<> opt = dao.find();

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

vavrなどを使用してカリー化すると、コードがさらにきれいになる可能性がありますが、まだ試していません。


63
v1(Java 8)に含まれるべきもののようです...まあ...
ycomp 2016

2
はい... Java 8では実際にそれを見逃したと思います。さらに...値が存在するときに何かをしたい場合は、「ifPresent()」を指定しました。値が存在するときに何かを実行し、存在しないときに他のことを実行したい場合は、「ifPresentOrElse(f1、f2)」を指定しました。しかし、それが存在しない状態で何かをしたいだけの場合はまだ不足しています(「ifNotPresent()」のようなものが適しています)。ifPresentOrElseを使用すると、後者の場合は何もしない現在の関数を使用する必要があります。
hbobenicio 2017年

フレームワークを導入できる場合は、Vavr(以前のJavaslang)とそのオプションを見てください。onEmptyメソッドがあります
Andreas

むしろ、Java 9を使用するか、if、elseを使用してください。vavrはあまり良くありません
先生

それ以外の場合、これはストレートのためだけに非常に複雑です!
jBarros 3520

37

私はあなたが単一のステートメントでそれを行うことができるとは思わない。より良いこと:

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

37
これは正しい答えかもしれませんが、これはどのようにnull小切手よりも優れていますか?私の観点からは、がないともっと悪いorElse...です。
daRich 2017年

4
@DaRich、コードの途中にあるnullを忘れることができます。これにより、NPEが発生します。しかしOptional、偶然を無視することはできません。それは常に明示的な(そして危険な)決定です。
Dherik 2018

17

Java 8の場合、SpringはifPresentOrElse「オプションを操作するユーティリティメソッド」から、必要なことを実現するために提供しています。例は次のとおりです。

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

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

11

これを複数のステートメントに分割する必要があります。これを行う1つの方法は次のとおりです。

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

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

別の方法(おそらく過剰設計)は、以下を使用することmapです。

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

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

obj.setAvailable便利に返される場合はobj、2番目の例を次のように簡単に実行できます。

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

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

9

まず、をdao.find()返すか、Optional<Obj>作成する必要があります。

例えば

Optional<Obj> = dao.find();

または、次のように自分で行うことができます。

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

これはOptional<Obj>、存在するOptional.empty()場合、または存在しない場合に戻ります。

それでは、解決策に取り掛かりましょう。

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

これはあなたが探しているワンライナーです:)


9
nullを返すと、オプションの目的が無効になります。OPの質問では、オブジェクトが見つからない場合に何をしなければならないかはあいまいです。私見では、新しくインスタンス化されたオブジェクトを返し、おそらくsetAvailableをfalseに戻す方がよいでしょう。もちろん、ここでOPは致命的なログを記録しました。つまり、彼はおそらく終了するつもりなので、実際には問題ではありません。
Somaiah Kumbera 2016

このソリューションはを返しますがObject、元の質問はを返すメソッドに対するものですOptional<Object>。私の(古い)答えは非常に似ていますが、このように異なります:stackoverflow.com/a/36681079/3854962
UTF_or_Death 2017

なぜ使用するのflatMapですか?
リノ

彼は値の代わりにオプションを返しているからです。FlatMapはOptional <Optional <X >>をOptional <X>に変換します
Aman Garg 2018

9

メソッドあります.orElseRunと呼ばれ.orElseGetます。

擬似コードの主な問題は、を.isPresent返さないことOptional<>です。ただし、メソッドを持つを.map返します。Optional<>orElseRun

1つのステートメントでこれを本当に実行したい場合は、次のことが可能です。

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

しかし、これはあなたが以前持っていたものよりもさらに不格好です。


3

たとえば、次のような「1行」のソリューションをいくつか思いつくことができました。

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

または

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

または

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

見た目はorElseRunあまり良くなく、もっと良いようなものですが、本当に1行のソリューションが必要な場合は、Runnableを使用したオプションを使用できます。


1

Java 8Optionalでは、次の方法で実行できます。

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

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

1

オプションがない場合にのみ副作用を実行したい方のために

つまり、ifAbsent()またはifNotPresent()ここに相当するものは、すでに提供されている優れた回答に対するわずかな変更です。

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

1
ifPresentOrElseにはJava9が必要です。
JL_SO20年


0

ifPresentOrElseは、nullpointerのケースも処理できます。簡単なアプローチ。

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

-2

dao.find()インスタンスを返すようにメソッドを変更することはできないと思いますOptional<Obj>ので、適切な自分で作成する必要があります。

次のコードはあなたを助けるはずです。OptionalActionif-elseメカニズムを提供するクラスを作成しました。

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
反対票を投じた場合は、コメントを残してください。これは私が答えを改善するのに役立ちます。
マイク2015年

反対票がここにコメントすることに同意します。これは、java7をjava8コードにリファクタリングしようとしていたのに対し、古いコードは8行で構成されていたためだと思います。そして、私それをあなたの提案に置き換えるとしたら、それは誰の助けにもならないでしょうが、事態を悪化させるだけです。
メンバーサウンド2015年

私はあなたの主張を理解していません。Java 7を8にリファクタリングしましたね?そして、それはどの程度まで事態を悪化させるでしょうか?このソリューションに欠陥は見られません。他の(または回避策)をオンにOptionalすることの全体的なポイントが理にかなっているかどうかを議論することができます。しかし、私はあなたの質問に正しく答え、実用的な例を提供しました。
マイク2015年

マイク、あなたの解決策は私には完全に有効なようです。とにかくOptionalAction、コードをjava8に移植できるようにするための回避策としてのような明示的なクラスを導入することは、java7でこれがすでに数ライナーしかない場合、少し過剰に設計されているように見えます。
メンバーサウンド2015年

2
オプションの<DbObject>オブジェクト=(v == null)?Optional.empty():Optional.of(v); 次のように書き換えることができます。Optional<DbObject>オブジェクト= Optional.ofNullable(v);
Geir 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.