1

这是正确的吗?

示例代码:

void foo(E param){

    if(param == null){
        // do this
    }else{
        // do that
    }
//...
}

首先,我阅读了一些帖子,人们在其中讨论允许变量为 null 会增加软件的复杂性 对没有 null 的语言的最佳解释,所以我想努力并忘记这一点来尝试回答这个问题。

想法(我的想法)

作为一个初学者,我的想法可能是错误的,但我希望你能帮我解决这个问题

我认为根据变量所在的上下文检查变量是否为 null 是正确的,让我用一个例子来解释一下:

使用不当:

我们的项目实现了命令模式,并且我们有一组指令。然后我们在控制台中写一个指令,我们必须解释它。我们有一个解释器类,它试图解析它,所以如果解释器找不到合适的指令,它会返回 null。

代码示例:

public class Interpreter {

    public static final String LINE_SEPARATOR = System.getProperty("line.separator");
    private static final List<Instruction> listInstructions = loadInstructions();


    /**
     * Generates a new instruction according to the user input
     * @param line - A string with the user input
     * @return The instruction read from the given line. If the instruction is not correct, then it returns null.
     */
    public static Instruction generateInstruction(String line) throws WrongInstructionFormatException
    {
        Instruction instru = null;
        Iterator<Instruction> it = listInstructions.iterator();
        int i = 0;
        while(it.hasNext() && i <9 && instru == null)
        {
            try
            {
                instru = it.next().parse(line);
            }
            catch(WrongInstructionFormatException e)
            {

            }
            finally
            {
                i++;
            }
        }
        return instru;
     }
//...




public class MainClass
{
    /**
     * MainFunction
     */
        public void mainFuction()
        {

            Instruction instru;
            while(true)
            {
                instru = Interpreter.generateInstruction(orden);
                if(instru == null) <------
                    // Error.
                else
                    instru.execute();

            }

        }
  }

好吧,在这种情况下,我认为我们实现了这个错误,因为解释器不能返回 null 而是异常,因为在我看来,这种上下文中的变量永远不会是 null我希望我很清楚,如果不是,那么我想稍后用你的评论来澄清

正确使用:(已编辑)

我们项目的主类有一组属性,它们都可以被实例化或不被实例化。取决于我们是否实例化了一个,或者两者都没有,将有一组函数来执行一个、两个或不执行任何操作。

public class MainClass extends Observable
{
    private A a;
    private B b; // This attribute allows you to start making a log in a txt file
    private C c; // This attribute allows you to start making a log in a DB

    public MainClass(A a){ //....}
    public MainClass(A a, B b) { // .....}
    public MainClass(A a, C c) { // .....}
    public MainClass(A a, B b, C c){ //.....}


    public void log(String update)
    {
        if(this.b != null) <-----
            // Save the update in the txt file
        if(this.c != null) <-----
            // Save the update in a db row
    }
}

那么,为什么这是正确的?我认为是正确的,因为属性可能具有空值,而不是像前一种情况那样。


这样就开始了讨论,如果他们有同样的疑问,我希望它可以帮助其他人。

4

3 回答 3

3

关于 null 的建议不是不检查它们,而是不生成或允许它们。显然,如果您的程序中有空值,则需要检查它们。

在您的第二个示例中,遵循建议的方法是找到一种方法来实现您的类,以便它没有可为空的字段。为什么类有可选属性?它对这些属性有什么作用?有没有办法构造类(也许通过将其分解为多个类)使其不具有可选属性?

在第二个示例中,您在if测试中使用属性的无效性。面向对象设计的标准做法是用多态性代替条件。你能试试吗?

编辑:好的,这就是我使用多态性解决第二种情况的方法。我已删除该字段a,因为在这种情况下它不相关。

public class MainClass extends Observable {
    private final Collection<? extends LogDestination> logDestinations;

    public MainClass() {
        logDestinations = Collections.emptySet();
    }

    public MainClass(B b) {
        logDestinations = Collections.singleton(new TextFileLog(b));
    }

    public MainClass(C c) {
        logDestinations = Collections.singleton(new DatabaseLog(c));
    }

    public MainClass(B b, C c) {
        logDestinations = Arrays.asList(new TextFileLog(b), new DatabaseLog(c));
    }

    public void log(String update) {
        for (LogDestination logDestination : logDestinations) {
            logDestination.log(update);
        }
    }
}

interface LogDestination {
    public void log(String update);
}

class TextFileLog implements LogDestination {
    private final B b;

    public TextFileLog(B b) {
        this.b = b;
    }

    @Override
    public void log(String update) {
        // Save the update in the txt file
    }
}

class DatabaseLog implements LogDestination {
    private final C c;

    public DatabaseLog(C c) {
        this.c = c;
    }

    @Override
    public void log(String update) {
        // Save the update in a db row
    }
}

在抽象的术语中,它所做的是将逻辑中唯一的替代方案一直推到构造函数中,以选择LogDestination要创建的 s 的形式。一旦它们被创建,它们就可以被完全统一地对待,多态性将一般调用分派给特定的方法。因为设置对象的不同方式有不同的构造函数,所以根本不需要任何条件逻辑。

于 2013-09-24T14:47:43.503 回答
2

当这是一种预期和/或可恢复的情况时,投掷Exceptionvs 返回null几乎完全是一个风格问题。你提出的论点是很好的区分,如果它们对你有意义,那么我说去吧。

当然,这并不是说您应该将异常用作首选的流程控制工具,但是在您所描述的那种情况下,它听起来非常合理。

于 2013-09-24T14:47:26.157 回答
1

我有一个包含 2000 多个课程的程序。假设每个类大约有 20 个函数,那就是 40,000 个函数。其中很多只有几行代码(例如:get/set 函数)。如果每个函数都检查空参数等,那么就会有很多代码专门用于验证(可以为许多正在处理的异常提供类似的参数)。此外,如果我只检查空值,我不妨进行全面验证以确保 (javadoc) 合同得到履行。例如,遍历传递给函数的对象列表、检查空值、范围问题等。请注意,这种方法会出现性能问题。

相反,我更喜欢有一个适用于整个项目的商定政策,即不允许任何函数的参数为​​ null 或返回值为 null,除非在其 javadoc 中指定(按合同设计)。调用者有责任在调用我的函数之前遵守 (javadoc) 合同并进行任何验证检查。但是,我会考虑对带有多个参数的构造函数进行验证检查(以确保不创建不完整的对象),以及一些处理输入参数的方式过于复杂的函数。请注意,我的函数的直接调用者不必在调用我的函数之前进行验证检查。相反,至少需要调用链中某处的其他函数之一来执行此操作。笔记:

于 2013-09-24T18:51:28.387 回答