51

クラス Family があるとします。人のリストが含まれています。各 (クラス) Person には (クラス) Address が含まれます。各 (クラス) Address には (クラス) PostalCode が含まれます。どの「中間」クラスも null にすることができます。

では、すべてのステップで null をチェックせずに PostalCode に到達する簡単な方法はありますか? つまり、次のデイジー チェーン コードを回避する方法はありますか? 「ネイティブ」な Java ソリューションがないことは知っていますが、誰かがライブラリなどを知っていることを望んでいました。(コモンズとグアバをチェックしましたが、何も表示されませんでした)

if(family != null) {
    if(family.getPeople() != null) {
        if(family.people.get(0) != null) {
            if(people.get(0).getAddress() != null) {
                if(people.get(0).getAddress().getPostalCode() != null) {
                    //FINALLY MADE IT TO DO SOMETHING!!!
                }
            }
        }
    }
}

いいえ、構造を変更することはできません。それは私が制御できないサービスからのものです。

いいえ、Groovy は使えません。便利な "Elvis" 演算子です。

いいえ、私はJava 8を待ちたくないです:D

このようなコードを書くことにうんざりした最初の開発者だとは信じられませんが、解決策を見つけることができませんでした。

4

12 に答える 12

20

コードは次のように動作します

if(family != null &&
  family.getPeople() != null &&
  family.people.get(0) != null && 
  family.people.get(0).getAddress() != null &&
  family.people.get(0).getAddress().getPostalCode() != null) { 
       //My Code
}

短絡評価のおかげで、これも安全です。最初の条件が false の場合は 2 番目の条件が評価されず、2 番目の条件が false の場合は 3 番目の条件が評価されないため、....そして NPE を取得しないため、それ。

于 2012-04-30T22:35:53.920 に答える
6

最も近い方法は、条件のショートカット ルールを利用することです。

if(family != null && family.getPeople() != null && family.people.get(0) != null  && family.people.get(0).getAddress() != null && family.people.get(0).getAddress().getPostalCode() != null) {
                    //FINALLY MADE IT TO DO SOMETHING!!!

}

ところで、事前に条件をテストする代わりに例外をキャッチするのは恐ろしい考えです。

于 2012-04-30T22:34:48.547 に答える
5

If, in case, you are using java8 then you may use;

resolve(() -> people.get(0).getAddress().getPostalCode());
    .ifPresent(System.out::println);

:
public static <T> Optional<T> resolve(Supplier<T> resolver) {
    try {
        T result = resolver.get();
        return Optional.ofNullable(result);
    }
    catch (NullPointerException e) {
        return Optional.empty();
    }
}

REF: avoid null checks

于 2016-10-12T04:19:57.640 に答える
1

null を使用する代わりに、「null オブジェクト」設計パターンのいくつかのバージョンを使用できます。例えば:

public class Family {
    private final PersonList people;
    public Family(PersonList people) {
        this.people = people;
    }

    public PersonList getPeople() {
        if (people == null) {
            return PersonList.NULL;
        }
        return people;
    }

    public boolean isNull() {
        return false;
    }

    public static Family NULL = new Family(PersonList.NULL) {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}


import java.util.ArrayList;

public class PersonList extends ArrayList<Person> {
    @Override
    public Person get(int index) {
        Person person = null;
        try {
            person = super.get(index);
        } catch (ArrayIndexOutOfBoundsException e) {
            return Person.NULL;
        }
        if (person == null) {
            return Person.NULL;
        } else {
            return person;
        }
    }
    //... more List methods go here ...

    public boolean isNull() {
        return false;
    }

    public static PersonList NULL = new PersonList() {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}

public class Person {
    private Address address;

    public Person(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        if (address == null) {
            return Address.NULL;
        }
        return address;
    }
    public boolean isNull() {
        return false;
    }

    public static Person NULL = new Person(Address.NULL) {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}

etc etc etc

次に、if ステートメントは次のようになります。

if (!family.getPeople().get(0).getAddress().getPostalCode.isNull()) {...}

次の理由から、最適ではありません。

  • クラスごとに NULL オブジェクトを作成するのに行き詰まっています。
  • これらのオブジェクトをジェネリックにするのは難しいため、使用したい各リスト、マップなどの null オブジェクト バージョンを作成することになります。
  • サブクラス化と、どの NULL を使用するかについて、おかしな問題が発生する可能性があります。

しかし、あなたが本当にあなたを嫌っているなら== null、これは逃げ道です.

于 2012-05-01T01:38:28.807 に答える
0

それほどクールなアイデアではありませんが、例外をキャッチするのはどうですか:

    try 
    {
        PostalCode pc = people.get(0).getAddress().getPostalCode();
    }
    catch(NullPointerException ex)
    {
        System.out.println("Gotcha");
    }
于 2012-04-30T22:33:48.103 に答える
0

私はちょうど同じものを探していました (私のコンテキスト: 自動的に作成された JAXB クラスの束、そしてどういうわけか私はこれらの長いデイジー チェーンを持っています.getFoo().getBar()...。常に、途中で呼び出しの 1 つが null を返し、NPE を引き起こします。

しばらく前にいじり始めたものは、リフレクションに基づいています。これをよりきれいに、より効率的にできると確信しています (たとえば、リフレクションをキャッシュ._allし、途中のメソッドがコレクションを返す場合、コレクションのすべての要素を自動的に反復するなどの「魔法の」メソッドを定義します)。 )。きれいではありませんが、おそらく誰かが私たちにもっと良いものがあるかどうか教えてくれるでしょう:

/**
 * Using {@link java.lang.reflect.Method}, apply the given methods (in daisy-chain fashion)
 * to the array of Objects x.
 * 
 * <p>For example, imagine that you'd like to express:
 * 
 * <pre><code>
 * Fubar[] out = new Fubar[x.length];
 * for (int i=0; {@code i<x.length}; i++) {
 *   out[i] = x[i].getFoo().getBar().getFubar();
 * }
 * </code></pre>
 * 
 * Unfortunately, the correct code that checks for nulls at every level of the
 * daisy-chain becomes a bit convoluted.
 * 
 * <p>So instead, this method does it all (checks included) in one call:
 * <pre><code>
 * Fubar[] out = apply(new Fubar[0], x, "getFoo", "getBar", "getFubar");
 * </code></pre>
 * 
 * <p>The cost, of course, is that it uses Reflection, which is slower than
 * direct calls to the methods.
 * @param type the type of the expected result
 * @param x the array of Objects
 * @param methods the methods to apply
 * @return
 */
@SuppressWarnings("unchecked")
public static <T> T[] apply(T[] type, Object[] x, String...methods) {
    int n = x.length;
    try {
        for (String methodName : methods) {
            Object[] out = new Object[n];
            for (int i=0; i<n; i++) {
                Object o = x[i];
                if (o != null) {
                    Method method = o.getClass().getMethod(methodName);
                    Object sub = method.invoke(o);
                    out[i] = sub;
                }
            }
            x = out;
        }
    T[] result = (T[])Array.newInstance(type.getClass().getComponentType(), n);
    for (int i=0; i<n; i++) {
            result[i] = (T)x[i];
    }
            return result;
    } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new RuntimeException(e);
    }
}
于 2013-04-26T22:14:26.410 に答える
0

めったにない場合は、nullチェックを無視して に頼ることができますNullPointerException。パフォーマンスの問題が発生する可能性があるため、「まれ」です (状況によって異なりますが、通常はスタック トレースを埋めるため、コストが高くなる可能性があります)。

それ以外は、1)nullをチェックしてそのコードをクリーンアップする特定のヘルパーメソッド、または2)リフレクションと次のような文字列を使用して一般的なアプローチを作成します。

checkNonNull(family, "people[0].address.postalcode")

実装は演習として残しました。

于 2012-04-30T22:37:18.803 に答える