检查变量是否等于/(不等于)为空是正确的吗?取决于背景?

时间:2013-09-24 14:40:26

标签: java null

这是对的吗?

示例代码:

void foo(E param){

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

首先,我阅读了一些帖子,其中讨论允许变量为空的人增加了软件Best explanation for languages without null的复杂性,所以我想做出努力并忘记这一点,试图回答这个问题。 / p>

思想(我的想法)

作为一个初学者,我的想法可能是错的,但我希望你帮助我清除它

我认为检查变量是否为null 是正确的,具体取决于该变量的上下文,让我用一个例子来解释:

使用不当:

我们的项目实现了Command模式,我们有一套指令。然后我们在控制台中写一条指令,我们必须解释它。我们有一个解释器类,它试图解析它,所以如果解释器找不到合适的指令,它将返回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但是异常,因为在我看来此上下文中的变量永远不能为空我希望我很清楚,如果不是,那么我想稍后澄清您的意见

正确使用:(已编辑)

我们项目的主要类有一组属性,可以从中实例化或不实例化。根据我们是否实例化了一个,两个或两个都没有一组函数来执行一个,两个或不执行任何操作。

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
    }
}

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


随着这开始讨论,如果他们有同样的疑问,我希望能帮助其他人。

3 个答案:

答案 0 :(得分:3)

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

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

在第二个示例中,您在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。一旦创建它们,就可以完全统一地处理它们,并使用多态性将一般调用分派给特定方法。因为设置对象的不同方式有不同的构造函数,所以根本不需要任何条件逻辑。

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

我有一个超过2000个课程的程序。假设每个类约有20个函数,那就是40,000个函数。他们中的很多人每行只有几行代码(例如:get / set functions)。如果每个函数都检查了空参数等,那么很多代码只用于验证(对于许多处理的异常,可以使用类似的参数)。此外,如果我只检查空值,我不妨进行完全验证以确保(javadoc)合同得到满足。例如,迭代传递给函数的对象列表,检查空值,范围问题等。请注意,该方法会出现性能问题。

相反,除了在javadoc(按合同设计)中指定之外,我更喜欢有一个适用于整个项目的商定政策,即不允许任何函数为参数赋值,或者返回值为null。在调用我的函数之前,调用者有责任遵守(javadoc)合同并进行任何验证检查。但是,我会考虑对带有多个参数的构造函数进行验证检查(以确保不创建不完整的对象),以及一些在处理输入参数方面过于复杂的函数。请注意,我的函数的直接调用者不必在调用我的函数之前进行验证检查。相反,在调用链的某个地方至少有一个其他功能需要这样做。 注意:我在网上看到关于处理这一切的正确方法存在很多争论。