クラスファミリーがあると想像してください。個人のリストが含まれています。各(クラス)Personには(クラス)アドレスが含まれます。各(クラス)アドレスには、(クラス)PostalCodeが含まれています。 「中間」クラスはnullにすることができます。
それで、すべてのステップでnullをチェックする必要なく、PostalCodeにアクセスする簡単な方法はありますか?つまり、次のデイジーチェーンコードを回避する方法はありますか? 「ネイティブ」ではないことを知っているJava解決策ですが、ライブラリや何かを知っている人がいるかどうかを望んでいました(Commons&Guavaをチェックして何も表示されなかった)
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
私がこのようなコードを書くことにうんざりする最初の開発者であるとは信じられませんが、解決策を見つけることができませんでした。
アイデア?
ありがとう
-
llappall
コードは次のように動作します
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
}
短絡評価のおかげで、これも安全です。最初の条件が偽の場合、2番目の条件は評価されないため、 2番目がfalseの場合、3番目は評価されません。.もしそうであれば、NPEを取得できません。
次の用途に使用できます。
product.getLatestVersion().getProductData().getTradeItem().getInformationProviderOfTradeItem().getGln();
オプションの同等物:
Optional.ofNullable(product).map(
Product::getLatestVersion
).map(
ProductVersion::getProductData
).map(
ProductData::getTradeItem
).map(
TradeItemType::getInformationProviderOfTradeItem
).map(
PartyInRoleType::getGln
).orElse(null);
あなたが得ることができる最も近いものは、条件文のショートカットルールを利用することです:
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!!!
}
ところで、事前に条件をテストする代わりに例外をキャッチするのは恐ろしい考えです。
まれな場合は、null
チェックを無視してNullPointerException
に依存することができます。可能性のあるパフォーマンスの問題が原因で「まれ」(通常、高価になる可能性のあるスタックトレースが記入されます)。
それ以外は1)nullをチェックしてそのコードをクリーンアップする特定のヘルパーメソッド、または2)リフレクションと文字列を使用して一般的なアプローチを行う:
checkNonNull(family, "people[0].address.postalcode")
実装は演習として残しました。
この投稿はほぼ5年前のものですが、NullPointerException
sを処理する方法についての古くからの質問に対する別の解決策があるかもしれません。
一言で言えば:
_end: {
List<People> people = family.getPeople(); if(people == null || people.isEmpty()) break end;
People person = people.get(0); if(person == null) break end;
Address address = person.getAddress(); if(address == null) break end;
PostalCode postalCode = address.getPostalCode(); if(postalCode == null) break end;
System.out.println("Do stuff");
}
_
まだ多くのレガシーコードが使用されているため、Java 8およびOptional
を使用することは常にオプションであるとは限りません。
深くネストされたクラス(JAXB、SOAP、JSON、それに名前を付ける...)があり、 Law of Demeter が適用されていない場合は、基本的にすべてをチェックして、可能なNPEがあるかどうかを確認する必要があります潜んでいる。
私の提案するソリューションは読みやすさを追求しているため、少なくとも3つ以上のネストされたクラスが含まれていない場合は使用しないでください(ネストされていると言っても、正式なコンテキストでは ネストされたクラス を意味しません)。 。コードは書かれている以上に読み取られるため、コードの左側を一目見ると、深くネストされたif-elseステートメントを使用するよりもその意味が明確になります。
Else部分が必要な場合は、次のパターンを使用できます。
_boolean prematureEnd = true;
end: {
List<People> people = family.getPeople(); if(people == null || people.isEmpty()) break end;
People person = people.get(0); if(person == null) break end;
Address address = person.getAddress(); if(address == null) break end;
PostalCode postalCode = address.getPostalCode(); if(postalCode == null) break end;
System.out.println("Do stuff");
prematureEnd = false;
}
if(prematureEnd) {
System.out.println("The else part");
}
_
特定のIDEは、ユーザーに指示しない限り、このフォーマットを解除します( この質問 を参照)。
条件文は反転する必要があります。コードを継続する必要があるときではなく、いつ中断するかをコードに伝えます。
もう1つ、コードはまだ壊れやすい傾向があります。コードの最初の行としてif(family.getPeople() != null && !family.getPeople().isEmpty())
を使用する必要があります。そうしないと、空のリストがNPEをスローします。
それほどクールなアイデアではありませんが、例外をキャッチするのはどうですか?
try
{
PostalCode pc = people.get(0).getAddress().getPostalCode();
}
catch(NullPointerException ex)
{
System.out.println("Gotcha");
}
場合によっては、Java8を使用している場合は使用できます。
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: nullチェックを回避する
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
sが本当に嫌いなら、これは解決策です。
ネストされたnullチェックを回避するための、私のお気に入りの単純なtry/catch ...
try {
if(order.getFulfillmentGroups().get(0).getAddress().getPostalCode() != null) {
// your code
}
} catch(NullPointerException|IndexOutOfBoundsException e) {}
私は同じことを探していました(私のコンテキスト:自動的に作成された多数のJAXBクラス、そしてどういうわけか.getFoo().getBar()...
のこれらの長いデイジーチェーンがあります。常に、時々、中間は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);
}
}