当前位置: 首页 > 面试题库 >

使用异常代替冗长的null检查是否可以接受?

萧德庸
2023-03-14
问题内容

我最近在一个项目中遇到此问题:有一个嵌套的对象链,例如:类A包含类B的实例变量,而该类又具有类C的实例变量,…,直到在类中有一个节点为止。 Z类树。

     -----      -----      -----      -----               ----- 
     | A | ---> | B | ---> | C | ---> | D | ---> ... ---> | Z |
     -----      -----      -----      -----               -----

每个类为其成员提供获取器和设置器。父A实例由XML解析器创建,并且链中的任何对象为null都是合法的。

现在想象一下,在应用程序中的某个点上,我们有一个对A实例的引用,并且仅当它包含Z对象时,我们才必须在其上调用一个方法。使用常规检查,我们得到以下代码:

    A parentObject;

    if(parentObject.getB() != null &&
        parentObject.getB().getC() != null &&
        parentObject.getB().getC().getD() != null &&
        parentObject.getB().getC().getD().getE() != null &&
        ...
        parentObject.getB().getC().getD().getE().get...getZ() != null){
            parentObject.getB().getC().getD().getE().get...getZ().doSomething();
    }

我知道异常不应该用于普通的控制流,但是我看到一些程序员代替了先前的代码:

    try {
        parentObject.getB().getC().getD().getE().get...getZ().doSomething();
    } catch (NullPointerException e){}

此代码的问题在于,在维护它时可能会造成混淆,因为它无法清楚显示允许哪些对象为null。但另一方面,它更为简洁,“伸缩性”更小。

这样做可以节省开发时间吗?如何重新设计API以避免此问题?

我唯一想避免长时间的空检查的方法是提供嵌套对象的空实例,并isValid为每个嵌套对象提供方法,但是这会不会在内存中创建许多不必要的对象?

(我使用过Java代码,但是相同的问题可以应用于C#属性)

谢谢。


问题答案:

我个人喜欢通过使用选项类型来完全避免此问题。通过将这些方法/属性返回的值调整为Option<T>而不是T,调用者可以选择他们希望如何处理无值的情况。

选项类型可以包含一个值,也可以不包含(但是选项本身永远不能为null),但是调用者不能简单地传递它而不解开值,因此它迫使调用者处理可能没有值的事实。

例如在C#中:

class A {
    Option<B> B { get { return this.optB; } }
}

class B {
    Option<C> C { get { return this.optC; } }
}

// and so on

如果调用者想抛出该异常,则它们仅检索该值,而无需显式检查是否存在一个值:

A a = GetOne();
D d = a.Value.B.Value.C.Value.D.Value; // Value() will throw if there is no value

如果调用方只想默认没有任何步骤的值,则可以执行映射/绑定/投影:

A a = GetOne();
D d = a.Convert(a => a.B) // gives the value or empty Option<B>
       .Convert(b => b.C) // gives value or empty Option<C>
       .Convert(c => c.D) // gives value or empty Option<D>
       .ValueOrDefault(new D("No value")); // get a default if anything was empty

如果呼叫者想在每个阶段默认,则他们可以:

A a = GetOne();
D d = a.ValueOrDefault(defaultA)
     .B.ValueOrDefault(defaultB)
     .C.ValueOrDefault(defaultC)
     .D.ValueOrDefault(defaultD);

Option目前不是C#的一部分,但我想会有一天。您可以通过引用F#库来获得实现,或者可以在Web上找到实现。如果您想要我的,请告诉我,我会把它发送给您。



 类似资料:
  • 我有以下代码: 为什么我对布尔变量“bool”的检查会导致异常?当它“看到”if语句不是真的时,它难道不应该跳过if语句吗?当我移除if语句或检查它是否为空时,异常就会消失。

  • 我有一个可以工作的DM命令,但如果我尝试DM一个关闭了DMs的人,会给出以下错误: 我如何让它检查用户是否打开了DMs?

  • 问题内容: 在Java中,有人告诉我在执行null检查时应使用==而不是.equals()。是什么原因呢? 问题答案: 他们是完全不同的两件事。比较变量包含的对象引用(如果有)。根据相等性的含义检查两个对象是否相等。根据它们的契约,两个不同的对象实例完全有可能“相等”。还有一个小细节,因为这是一个方法,所以如果你尝试在引用上调用它,则会得到一个。 例如:

  • 问题内容: 试一试无用的东西只是看看此代码是否引发了特定的异常,这是一个好方法吗? 我想在引发异常时做点什么,否则就什么也不做。 有太多的先决条件要测试,构造函数BigDecimal()始终检查所有条件,因此这似乎是最简单的方法。 问题答案: 通常,应避免这种做法。但是,由于没有实用程序方法,所以这是要走的路。 正如Peter Tillemans在评论中指出的那样,将此代码放在称为的实用程序方法中

  • 问题内容: 如何用Java代码检查当前的JVM是否有无限强度的加密可用? 问题答案: 我认为您可能可以使用Cipher.getMaxAllowedKeyLength(),同时还将您使用的密码与已知的“良好”安全密码(例如AES)列表进行比较。 这是一篇参考文章,列出了自Java 1.4起当前最大的密钥大小管辖权限制(除非法律也有所改变,否则这些可能没有改变-参见下文)。 如果您在有密码进出口限制的

  • 我正试图在android中创建一个干净的复选框代码,但如果我只是按下按钮而没有选中复选框,则会出错这是清理复选框的代码: 这是我在重置按钮上输入的代码 该复选框是一个声明: 显然,如果未选中复选框并出现错误,tem.getItemId为null。 注:如果你不知道答案或不明白,最好询问或继续,然后投否决票。让其他有帮助的人看到这个问题