使用Java7避免嵌套数据中出现空值的更好方法
我必须分析一个巨大的数据流,其中经常包含不完整的数据。目前,代码在多个级别上充斥着空检查,因为任何级别上都可能有不完整的数据 例如,我可能需要检索:使用Java7避免嵌套数据中出现空值的更好方法,java,json,nullpointerexception,null,retrofit,Java,Json,Nullpointerexception,Null,Retrofit,我必须分析一个巨大的数据流,其中经常包含不完整的数据。目前,代码在多个级别上充斥着空检查,因为任何级别上都可能有不完整的数据 例如,我可能需要检索: Model.getDestination().getDevice().getName() 我尝试创建一个方法,尝试将空检查减少为一个方法,从而输入: IsValid(Model.getDestination()、Model.getDestination().getDevice()、Model.getDestination().getDevice()
Model.getDestination().getDevice().getName()
我尝试创建一个方法,尝试将空检查减少为一个方法,从而输入:
IsValid(Model.getDestination()、Model.getDestination().getDevice()、Model.getDestination().getDevice().getName())
此方法失败,因为它在发送所有参数之前对其进行求值,而不是像这样一次检查每个参数
Model.getDestination()!=null&&Model.getDestination().getDevice()!=空和等
但是有没有一种方法可以让我传入Model.getDestination().getDevice().getName()
,然后在每个级别上进行检查,而不必在通过之前对其进行评估或拆分
我真正希望它做的是,如果存在null/nullexception,它应该悄悄地返回“”,并继续处理传入的数据
我知道在Java8中有很多方法可以优雅地做到这一点,但我还是坚持使用Java7您可以使用
try catch
。因为在您的案例中不需要处理,比如
try{
if(IsValid(Model.getDestination(), Model.getDestination().getDevice(), Model.getDestination().getDevice().getName())){
}catch(Exception e){
//do nothing
}
或者,您可以通过只传递模型
对象来改进isValid
方法
boolean isValid(Model model){
return (model != null && model.getDestination() != null && model.getDestination().getDevice() != null && model.getDestination().getDevice().getName() != null)
}
选项1-if语句
您已经在问题中提供了它。我认为像下面这样使用amif语句是完全可以接受的:
Model.getDestination() != null && Model.getDestination().getDevice() != null && etc
选项2-javax验证和检查结果-发送前
您可以使用javax验证
见:
您可以使用@NotNull
注释所需的字段
然后可以使用编程验证
您可以检查验证结果以查看是否存在问题
示例:
所以在你的课堂上你会做:
@NotNull
Public String Destination;
您可以将对象馈送到验证程序:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Model>> violations = validator.validate(Model);
for (ConstraintViolation<User> violation : violations) {
log.error(violation.getMessage());
}
选项4-仅使用try/catch
由于异常的缓慢性,每个人都讨厌这一个。所以您希望简化模型。getDestination().getDevice().getName()
。首先,我想列出一些不应该做的事情:不要使用异常。不要编写IsValid
方法,因为它根本不起作用,因为所有函数(或方法)在Java中都是严格的:这意味着每次调用函数时,所有参数都会在传递给函数之前进行求值
在Swift中,我只需编写let name=Model.getDestination()?.getDevice()?.getName()??“”
。在Haskell中,它就像name>=getDevice>=getName)只是“
(假设可能是monad)。这与Java代码的语义不同:
if(Model.getDestination() && Model.getDestination().getDevice() && Model.getDestination().getDevice().getName() {
String name = Model.getDestination().getDevice().getName();
System.out.println("We got a name: "+name);
}
因为这个代码段调用了getDestination()
4次,getDevice()
3次,getName()
2次。这不仅仅意味着性能:1)它引入了竞争条件。2) 如果其中任何一种方法都有副作用,您不希望多次调用它们。3) 这使得调试变得更加困难
唯一正确的方法是这样:
Destination dest = Model.getDestination();
Device device = null;
String name = null;
if(dest != null) {
device = dest.getDevice();
if(device != null) {
name = device.getName();
}
}
if(name == null) {
name = "";
}
Model model = new Model(new Destination(null));
Destination destination = model.getDestination().getValue(); // destination is not null
Device device = model.getDestination().getDevice().getValue(); // device will be null, no NPE
String name = destination.getDevice().getName().getValue(); // name will be null, no NPE
NavDevice navDevice = model.getDestination().getDevice(); // returns an ever non-null NavDevice, not a Device
String name = navDevice.getValue().getName(); // cause an NPE by circumventing the navigation structure
此代码将name设置为Model.getDestination().getDevice().getName()
,或者如果这些方法调用中的任何一个返回null,则将name设置为“”。我认为正确性比可读性更重要,特别是对于生产应用程序(甚至对于示例代码IMHO)。上述Swift或Haskell代码相当于该Java代码
如果你有一个生产应用程序,我想你已经在做类似的事情了,因为所有与之根本不同的东西都很容易出错
每个更好的解决方案都必须提供相同的语义,并且不能多次调用任何方法(getDestination、getDevice、getName)。
也就是说,我不认为用Java7可以简化代码太多
当然,您可以做的是缩短调用链:例如,如果您经常需要此功能,您可以在目的地
上创建一个方法getDeviceName()
。这是否使代码更具可读性取决于具体情况
强制您在这个低级别上编码也有好处:您可以执行公共子表达式消除,并且您将看到它的优点,因为它将使代码更短。例如,如果您有:
String name1 = Model.getDevice().getConnection().getContext().getName();
String name2 = Model.getDevice().getConnection().getContext().getLabel();
您可以将它们简化为
Context ctx = Model.getDevice().getConnection().getContext();
String name1 = ctx.getName();
String name2 = ctx.getLabel();
第二个代码段有3行,而第一个代码段只有两行。但如果展开这两个代码段以包含空检查,您将看到第二个版本实际上要短得多。(我现在不做是因为我懒惰。)
因此(关于可选链接),Java7将使性能感知编码器的代码看起来更好,而更多的高级语言则会鼓励编写慢代码。(当然,您也可以在更高级的语言中消除公共子表达式(您可能应该这样做),但根据我的经验,大多数开发人员更不愿意使用高级语言。而在汇编程序中,一切都是优化的,因为更好的性能通常意味着您必须编写更少的代码,并且您编写的代码更容易理解。)
一句完美的话,我们都会使用内置可选链接的语言,并且我们都会负责任地使用它,而不会造成性能问题和竞争条件。我也遇到了一个类似的问题,即深度嵌套的结构,如果我有机会引入额外的结构来导航底层数据,我想,我已经做到了
这就是C#,同时它有一个save导航/Elvis操作符,我们将用Java徒劳地等待它(建议用于Java7,但顺便说一句,Groovy有它)。看起来也有争论,即使你
Model model = new Model(new Destination(null));
Destination destination = model.getDestination().getValue(); // destination is not null
Device device = model.getDestination().getDevice().getValue(); // device will be null, no NPE
String name = destination.getDevice().getName().getValue(); // name will be null, no NPE
NavDevice navDevice = model.getDestination().getDevice(); // returns an ever non-null NavDevice, not a Device
String name = navDevice.getValue().getName(); // cause an NPE by circumventing the navigation structure
class Destination {
private final Device device;
public Destination(Device device) {
this.device = device;
}
public Device getDevice() {
return device;
}
}
class Device {
private final String name;
private Device(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Model {
private final Destination destination;
private Model(Destination destination) {
this.destination = destination;
}
public NavDestination getDestination() {
return new NavDestination(destination);
}
}
class NavDestination {
private final Destination value;
private NavDestination(Destination value) {
this.value = value;
}
public Destination getValue() {
return value;
}
public NavDevice getDevice() {
return new NavDevice(value == null ? null : value.getDevice());
}
}
class NavDevice {
private final Device value;
private NavDevice(Device value) {
this.value = value;
}
public Device getValue() {
return value;
}
public NavName getName() {
return new NavName(value == null ? null : value.getName());
}
}
class NavName {
private final String value;
private NavName(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}