想象一下,我有一个班级家庭。它包含一个人员列表。每个(类)Person包含一个(类)地址。每个(类)地址包含一个(类)PostalCode。任何“中间”类都可以为null。
那么,有没有一种简单的方法来获取PostalCode而不必在每一步都检查null?即,有没有办法避免以下菊花链代码?我知道没有“原生”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,它是方便的“猫王”操作员。
不,我不想等待Java 8:D
我无法相信我是第一个生病的人,厌倦了编写这样的代码,但我找不到解决方案。
想法?
由于
-
llappall
答案 0 :(得分:14)
您的代码与
的行为相同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
}
感谢 short circuiting evaluation ,这也是安全的,因为如果第一个为假,则不会评估第二个条件,如果第二个为假,则不会评估第三个条件, ....你不会得NPE,因为如果它。
答案 1 :(得分: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!!!
}
顺便说一句,捕捉异常而不是提前测试条件是一个可怕的想法。
答案 2 :(得分:3)
如果很少见,您可以忽略null
次检查并依赖NullPointerException
。 “罕见”由于可能的性能问题(取决于,通常会填写堆栈跟踪,这可能是昂贵的)。
除了1)一个特定的辅助方法,它检查null以清理该代码或2)使用反射和类似字符串的通用方法:
checkNonNull(family, "people[0].address.postalcode")
实施作为练习。
答案 3 :(得分:2)
这不是一个很酷的主意,但如何捕获异常:
try
{
PostalCode pc = people.get(0).getAddress().getPostalCode();
}
catch(NullPointerException ex)
{
System.out.println("Gotcha");
}
答案 4 :(得分:2)
您可以用于:
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);
答案 5 :(得分:1)
您可以使用某种版本的“null object”设计模式,而不是使用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
,这是一个出路。
答案 6 :(得分:0)
我只是在寻找相同的东西(我的上下文:一堆自动创建的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);
}
}
答案 7 :(得分:0)
如果您使用的是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();
}
}
答案 8 :(得分:0)
虽然这篇文章差不多有五年了,但我可能还有另一个解决方法来处理如何处理NullPointerException
的古老问题。
简而言之:
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个或更多嵌套类(当我说嵌套时,我不是指Nested classes在正式的背景下)。由于代码的读取比编写代码更多,因此快速浏览代码的左侧部分将比使用深层嵌套的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会破坏此格式,除非您不指示它们(请参阅this question)。
您的条件必须反转 - 您应该告诉代码何时应该中断,而不是应该继续。
还有一件事 - 您的代码仍然容易破损。您必须使用if(family.getPeople() != null && !family.getPeople().isEmpty())
作为代码中的第一行,否则空列表将抛出NPE。
答案 9 :(得分:0)
和我最喜欢的简单的try / catch,以避免嵌套的空检查...
try {
if(order.getFulfillmentGroups().get(0).getAddress().getPostalCode() != null) {
// your code
}
} catch(NullPointerException|IndexOutOfBoundsException e) {}
答案 10 :(得分:0)
如果可以使用Groovy进行映射,它将清理语法,并使代码看起来更简洁。 Groovy与Java共存时,您可以利用groovy进行映射。
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!!!
}
}
}
}
}
相反,您可以这样做
if(family?.people?[0]?.address?.postalCode) {
//do something
}
或者如果您需要将其映射到其他对象
somobject.zip = family?.people?[0]?.address?.postalCode