web-dev-qa-db-ja.com

「get」呼び出しのチェーンをチェックしてnullを探します

次のコマンドを実行したいとします。

house.getFloor(0).getWall(WEST).getDoor().getDoorknob();

NullPointerExceptionを回避するには、次の場合に次のことを行う必要があります。

if (house != null && house.getFloor(0) && house.getFloor(0).getWall(WEST) != null
  && house.getFloor(0).getWall(WEST).getDoor() != null) ...

これをよりエレガントに行う方法または既存のUtilsクラスはありますか?次のようにしましょう。

checkForNull(house.getFloor(0).getWall(WEST).getDoor().getDoorknob());
39
user321068

最良の方法は、チェーンを回避することです。デメテルの法則(LoD)に精通していない場合は、私の意見ではそうすべきです。あなたは、ビジネスが何も知らないクラスと過度に親密なメッセージチェーンの完璧な例を挙げました。

デメテルの法則: http://en.wikipedia.org/wiki/Law_of_Demeter

16

あなたができない場合は、選択した回答に記載されているように、デメテルの法則(LoD)に違反することを避け、 Java 8 導入 オプション 、あなたのようなgetのチェーンでnullを処理するのがおそらくベストプラクティスでしょう。

Optionalタイプを使用すると、複数のマップ操作(get呼び出しを含む)を連続してパイプ処理できます。ヌルチェックは、内部で自動的に処理されます。

たとえば、オブジェクトが初期化されていない場合、print()は作成されず、例外はスローされません。それはすべて私たちがボンネットの下で優しく取り扱われます。オブジェクトが初期化されると、印刷が行われます。

System.out.println("----- Not Initialized! -----");

Optional.ofNullable(new Outer())
        .map(out -> out.getNested())
        .map(nest -> nest.getInner())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //no print

System.out.println("----- Let's Initialize! -----");

Optional.ofNullable(new OuterInit())
        .map(out -> out.getNestedInit())
        .map(nest -> nest.getInnerInit())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //will print!

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

class OuterInit {
    NestedInit nested = new NestedInit();
    NestedInit getNestedInit() {
        return nested;
    }
}
class NestedInit {
    InnerInit inner = new InnerInit();
    InnerInit getInnerInit() {
        return inner;
    }
}
class InnerInit {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

したがって、ゲッターチェーンを使用すると、次のようになります。

Optional.ofNullable(house)
        .map(house -> house.getFloor(0))
        .map(floorZero -> floorZero.getWall(WEST))
        .map(wallWest -> wallWest.getDoor())
        .map(door -> wallWest.getDoor())

返されるのはOptional<Door>のようなもので、null例外を心配することなくはるかに安全な作業を可能にします。

49
Johnny

ゲットのチェーンをチェックするために ヌルクロージャからコードを呼び出す必要があるかもしれません。クロージャ呼び出しコードは次のようになります。

public static <T> T opt(Supplier<T> statement) {       
    try {
        return statement.get();
    } catch (NullPointerException exc) {
        return null;
    }   
}

そして、次の構文を使用してそれを呼び出します。

Doorknob knob = opt(() -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

このコードもタイプセーフであり、一般的に意図したとおりに機能します。

  1. チェーン内のすべてのオブジェクトがそうでない場合は、指定されたタイプの実際の値を返します ヌル
  2. 戻り値 ヌル チェーン内のオブジェクトのいずれかが ヌル

あなたは置くことができます opt メソッドを共有utilクラスに入れ、アプリケーションのあらゆる場所で使用します。

18
Roman Seleznov

もちろん、式全体をtry-catchブロックにまとめることもできますが、それは悪い考えです。よりクリーンなものは Nullオブジェクトパターン です。これにより、家に0階がない場合、通常の階のように機能するが、実際のコンテンツがない階が返されます。床は、持っていない壁を求められたときに、同様の「ヌル」壁などを返します。

4
Carl Manaster

論理的にnullにできないものはそうではないことを確認してください。たとえば、家には常に西の壁があります。状態でこのような例外を回避するために、予期する状態が存在するかどうかを確認するメソッドを使用できます。

if (wall.hasDoor()) {
   wall.getDoor().etc();
}

これは本質的にヌルチェックですが、常にそうであるとは限りません。

重要なのは、nullがある場合に備えて何かをする必要があるということです。例-returnまたはIllegalStateExceptionをスローします

そして、あなたがすべきではないこと-NullPointerExceptionを捕まえないでください。ランタイム例外はキャッチするためのものではありません-それらから回復できることは期待されていません。また、ロジックフローの例外に依存することもお勧めしません。実際に何かがnullになることを期待しておらず、NullPointerExceptionをキャッチ(およびログ記録)するとします。その時点で多くのものがnullになる可能性があるため、これはあまり有用な情報ではありません。

4
Bozho

nullPointer try/catchをサプライヤーと実装すると、getのすべてのチェーンを送信できます

public static <T> T getValue(Supplier<T> getFunction, T defaultValue) {
    try {
        return getFunction.get();
    } catch (NullPointerException ex) {
        return defaultValue;
    }
}

そしてそれをこのように呼びます。

ObjectHelper.getValue(() -> object1.getObject2().getObject3().getObject4()));
1
Omar Ruiz

非常に古い質問ですが、それでも私の提案を追加しています:

HouseからDoorKnobを取得する代わりに、呼び出し元のコードからこのクラスにDoorKnobを提供するか、この目的専用の中央ルックアップ機能(DoorKnobサービスなど)を作成することをお勧めします。

0
Adriaan Koster

これを容易にするcheckForNullメソッドを作成することはできません(これは、Javaでメソッドの呼び出しと引数の評価がどのように機能するかではありません)。

チェーンされたステートメントを複数のステートメントに分割し、すべてのステップでチェックできます。ただし、おそらくより良い解決策は、これらのメソッドが最初にnullを返さないようにすることです。 Null Object Pattern と呼ばれるものがあり、代わりに使用することをお勧めします。

関連する質問

0