web-dev-qa-db-ja.com

オプションが空のときに戻る方法は?

オプション はJava標準ライブラリにあります。しかし、私が実行し続ける基本的な問題が1つあります。最高の(読みやすく、理解しやすい、きれいな、最短の)方法:

オプションが空の場合にメソッドから戻る方法

オプションの数とコードブロックのサイズのさまざまな組み合わせで機能する一般的なソリューションを探しています。

次の例では、私が何を意味するかを示します。

void m1() {
    // When I get an optional:
    Optional<String> o = getOptional();

    // And want to return if it's empty
    if (!o.isPresent()) return;

    // In the whole rest of the method I have to call Optional.get 
    // every time I want the value:
    System.out.println(o.get());

    // Which is pretty ugly and verbose!
}


void m2() {
    // If I instead return null if a value is absent:
    String s = getNullabe();
    if (s == null) return;

    // Then I can use the value directly:
    System.out.println(s);
}

この質問は、上記の両方の例の優れた側面をどのように得るかに関するものです。

残りの例では、これについてさらに説明します。

void m3() {
    // If I on the other hand want to throw on empty that's pretty and compact:
    String s = getOptional()
        .orElseThrow(IllegalStateException::new);

    System.out.println(s);
}

void m4() {
    Optional<String> o = getOptional();
    if (!o.isPresent()) return;

    // I can of course declare a new variable for the un-optionalised string:
    String s = o.get();

    System.out.println(s);

    // But the old variable still remains in scope for the whole method 
    // which is ugly and annoying.
    System.out.println(o.get());
}


void m5() {
    // This is compact and maybe pretty in some ways:
    getOptional().ifPresent(s -> {
        System.out.println(s);

        // But the extra level of nesting is annoying and it feels 
        // wrong to write all the code in a big lambda.

        getOtherOptional().ifPresent(i -> {
            // Also, more optional values makes it really weird and 
            // pretty hard to read,  while with nullables I would 
            // get no extra nesting, it would looks good and be 
            // easy to read.
            System.out.println("i: " + i);

            // It doesn't work in all cases either way.
        });
    });
}


Optional<String> getOptional() {
    throw new UnsupportedOperationException();
}

Optional<Integer> getOtherOptional() {
    throw new UnsupportedOperationException();
}

String getNullabe() {
    throw new UnsupportedOperationException();
}

オプションが空の場合、メソッドの残りの部分でgetを使用せずに、追加の変数を宣言せずに、ブロックのネストのレベルを追加せずに、メソッドから戻るにはどうすればよいですか?

または、それをすべて入手できない場合、この状況に対処する最善の方法は何ですか?

18
Lii

orElse(null)を使用できます:

String o = getOptional().orElse(null);
if (o == null) {
    return;
}
23
dnault

代わりにifPresentメソッドとmapメソッドを使用できます。関数が無効であり、副作用を実行する必要がある場合は、ifPresentを使用できます。

optional.ifPresent(System.out::println); 

別のメソッドの戻り値がOptionalに依存している場合、そのメソッドもOptionalを返し、mapメソッドを使用する必要がある場合があります

Optional<Integer> getLength(){
    Optional<String> hi = Optional.of("hi");
    return hi.map(String::length)
}

ほとんどisPresentおよびgetを呼び出すとき、Optionalを誤用しています。

8
Sleiman Jneidi

使用しているifPresentでは、新しいラムダを作成する必要はありません。メソッド参照を使用するだけです。

getOptional().ifPresent(System.out::println);

ただし、これは2つのオプションの存在を条件にしたい場合には実際には解決しません。しかし、代わりに

// And want to return if it's empty
if (!o.isPresent()) return;

ネストされた場合でもうまく機能する条件を単に反転させないのはなぜですか?戻り値を明示的にする必要はありません。

if (o.isPresent()) {
  System.out.println(o.get());
  if (oo.isPresent()) {
    System.out.println(oo.get());
  }
}

ただし、この種のユースケースは、null許容値とは対照的に、Optionalから実際に利益を得ていないことを示唆しています。一般に、isPresentとgetを使用している場合、Optionalは実際にはそれほど多くを取得していない可能性があります(値が欠落している場合を考慮することを強制する場合を除く)。 ifPresent、map、filter、およびその他の「より機能的な」メソッドの使用は、Optional値のより一般的な使用法です。


ただし、いずれにしても、Optionalを約束している場合はnullを返さないでください。オブジェクトが期待されるときにnullを返すことは完全に合法ですが、Optionalのポイントは、nullをチェックする必要を避けることです。しないでください:

Optional<String> getOptional() {
    return null;
}

しかし代わりに:

Optional<String> getOptional() { 
  return Optional.empty();
}

さもなければ、あなたはやらなければならないことになります:

Optional<String> o = getOptional();
if (o != null && o.isPresent()) {
  // ...
}

これは実際に同じ種類のことを2回行っているだけです。 Optionalを使用するか、null許容値を使用しますが、両方を使用しないでください!

5
Joshua Taylor

それは素晴らしいトピックです。私たちはみんなプログラミングの機能的なスタイルが大好きです!

多くの場合、メソッドの実装を開始すると、上部にオプションの権利が与えられます。この時点で、空のオプションを処理するためにできる最善の方法について疑問に思い始めます。

ステップ1-探索してanylyze

public void processMedia(String mediaClassName, String mediaName) {

    // THAT MIGHT BE YOUR FIRST IDEA
    MediaClass mediaClass = mediaClassFinder.find(mediaClassName).orElse(null); 

    // RETURNING ON NULL CONDITION LIKE THE BELOW CAN BE ALRIGHT,
    // BUT POSSIBLY YOU CAN DO BETTER
    if (mediaClass == null) {
        return;
    }
    Optional<Media> media = mediaFinder.find(mediaClass.getId(), mediaName);

    // do processing

    // render the processed object
}

ステップ2最適なアプローチは、実装のさまざまな部分を別々のメソッドに抽出し、それらを機能的なスタイルで連結することです。この演習の副作用として、アプリケーションのインターフェイスと構造が大幅に改善される可能性があります。それがリファクタリングの仕組みです。以下を見てください。明示的なnullの割り当てはなく、余分なリターンポイントはどこにもありません。そして、コーディングが楽しくなります。

public void processMedia(String mediaClassName, String mediaName) {
    mediaClassFinder.find(mediaClassName)
        .flatMap(mediaClass -> mediaFinder.find(mediaClass.getId(), mediaName))
        .map(this::compress)
        .ifPresent(this::render);
}
private Media compress(Media media) {
    // compress media implementation
    return media;
}
private void render(Media media) {
    // render media implementation
}

私の例を気に入っていただければ幸いです:)

1
mwdev

あなたが求めていることは実際には可能だとは思いませんが、文字列で直接動作するすべてのコードを取得して関数にラップすることをお勧めします。したがって、関数は次のようになります。

_void m4() {
    Optional<String> o = getOptional();
    if (!o.isPresent()) return;

    doThings(o.get());
}

void doThings(String s){
    System.out.println(s);
    //do whatever else with the string.
}
_

この方法では、スコープ内に文字列があるだけで、.get()にアクセスするたびに呼び出す必要はありません。

0
Luka Jacobowitz