【问题标题】:calling function should return default value, if object (or any function result) is null如果对象(或任何函数结果)为空,则调用函数应返回默认值
【发布时间】:2013-07-25 08:02:31
【问题描述】:

是否可以将以下代码包装在可重用函数中?

编辑: 这只是一个例子,我想要一个适用于所有递归深度的工作解决方案

我想要的是生成以下代码:

if (MyObject o == null || 
    o.getSubObject() == null ||
    o..getSubObject().getSubSubObject() == null /*|| 
    ... */)
    return defaultValue;
return o.getSubObject().getSubObject()/*...*/.getDesiredValue();

通过调用类似的东西

Object defaultValue = null;
Object result = NullSafeCall(o.getSubObject().getSubObject()/*...*/.getDesiredValue(), defaultValue);

第二个代码块只是一个想法,我不在乎它看起来如何,我想要的是,如果需要,我可以在调用更深层次的函数之前避免所有 null 检查...

注入可以适当地做到这一点,但是没有其他/更简单的解决方案吗?以前从未看过注射...

EDIT2: 另一种语言的示例:http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator

【问题讨论】:

  • 我对每一个解决方案都感兴趣......虽然这个问题出现的原因是一个 android 项目......

标签: java null null-check


【解决方案1】:

并非如此,您以这种方式编写的任何代码都会看起来很糟糕和/或使用非常缓慢的反射。除非您使用可以理解和更改您编写的代码的实际 Java 预处理器。

更好的(但与相当多的重构相关)方法是确保有问题的值不可能为空。例如,您可以将各个访问器(getSubObject()getDesiredValue())修改为从不返回 null:让它们返回默认值。默认值上的访问器依次返回默认值。

【讨论】:

  • 这是一种解决方案,我知道...实际上,我不在乎这些函数是否返回 null,我想要的只是一个默认(甚至可能是 null)值作为结果,如果有任何对象(无论哪个级别)为空...以避免 NullPointerException...我只是认为可能有一个,就像你说的,例如预处理器解决方案或其他东西...在我的情况下,我正在访问一个抽象类的实例,只能在特定点创建……而在此之前,我不想无缘无故地创建一个空实例……
  • 类似这样的东西:groovy.codehaus.org/Operators#Operators-SafeNavigationOperator 不幸的是,这是一种不同的语言。
  • 顺便说一句,发现一篇博客文章为 Java7 提出了这个建议 - 显然它没有进入。blog.joda.org/2009/01/…
  • 我也认为这是对 java7 的建议,但据我在那里读到的,它从未进入它......但这正是我想要的......
【解决方案2】:

你可以这样做

public static Object  NullSafeCall(MyObject o,Object defaultValue){
    if ( o == null || o.getSubObject() == null)
    {
        return defaultValue;
    }
    else 
    {
        return o.getSubObject().getDesiredValue();
    }
}

现在你可以如下调用这个方法

 Object result = NullSafeCall(o, defaultValue);

【讨论】:

  • 谢谢,我知道这个特定的解决方案,我想要的是一个函数/预编译器或任何适用于所有类和所有深度的语法......所有对象的可重用解决方案。 ..
【解决方案3】:

我建议只替换

Object result = NullSafeCall(o.getSubObject().getDesiredValue(), defaultValue);

Object result = (o == null || o.subObject == null) ? defaultVlue : o.getSubObject().getDesiredValue();

只有在可以重复使用的情况下才创建方法......

【讨论】:

  • 这只是一个特定的解决方案(用它制作一个单行)......不可重复使用......我编辑了我的主要帖子以更清楚我想要什么
【解决方案4】:

你想要的都是不可能的。必须了解使用以下语法:Object result = NullSafeCall(o.getSubObject().getSubObject() ...); o.getSubObject().getSubObject() 的部分将在任何控制传递给函数/方法从而引发异常之前进行评估。

在执行此类代码之前需要有某种类型的上下文。我能想到的最接近这个的,可以使用匿名内部类来完成,如下例所示:

// intended to be implemented by an anonymous inner class
interface NullSafeOperation<T> {
    public T executeSafely();
};

// our executor that executes operations safely
public static class NullSafeExecutor<T> {
    public NullSafeExecutor() {}

    public T execute(T defaultValue, NullSafeOperation<T> nso) {
        T result = defaultValue;
        try {
            result = nso.executeSafely();
        } catch(NullPointerException e) {
            // ignore
        }
        return result;
    }

    // utility method to create a new instance and execute in one step
    public static <T> T executeOperation(T defaultValue, NullSafeOperation<T> nso) {
        NullSafeExecutor<T> e = new NullSafeExecutor<T>();
        T result = e.execute(defaultValue, nso);
        return result;
    }
}


public static void main(String[] args) {

    final String aNullString = null;

    String result = NullSafeExecutor.executeOperation("MyDefault", new NullSafeOperation<String>() {
        @Override
        public String executeSafely() {
            // trying to call a method on a null string
            // it will throw NullPointerException but it will be catched by the executor
            return aNullString.trim(); 
        }
    });

    System.out.println("Output = " + result); // prints: Output = MyDefault
}

【讨论】:

  • 我知道这种语法不起作用,它只显示我想要的...您可以将语法更改为NullSafeCall("o.getSubObject().getDesiredValue()", defaultValue);...这很好用...我可以之后解析字符串并解析子函数并检查它们的所有返回值(通过反射)......如上所述,我的问题中的代码只是演示了我想要的......
  • 我明白了。我只是想指出,如果没有上下文,那么这种方法是不可能的。上面的解决方案提供了一个:您的代码被包装在一个内部类中。如您所说,使用字符串版本和反射提供了另一种上下文。
  • 你是对的......但如果编译器能够解释这样的语法,它就不需要上下文......你的解决方案是一种解决方法,它可以工作,但它会不让我编写可以自动生成的不必要代码……在这种情况下,在最外部的函数调用周围使用 try/catch 也可以解决问题……
【解决方案5】:

Java8 有助于以我怀疑的良好性能获得最接近您的语法;

// Evaluate with default 5 if anything returns null.
int result = Optional.eval(5, o, x->x.getSubObject(), x->x.getDesiredValue());

这可以通过这个实用程序类来完成;

class Optional {
    public static <T, Tdef, T1> Tdef eval(Tdef def, T input, Function<T,T1> fn1,
                                          Function<T1, Tdef> fn2)
    {
        if(input == null) return def;
        T1 res1 = fn1.apply(input);
        if(res1 == null) return def;
        return fn2.apply(res1);
    }
}

遗憾的是,您需要为链中的方法调用次数定义一个单独的 eval(),因此您可能需要定义一些,但编译时类型安全且可重用,几乎可以用于任何调用/类型。

【讨论】:

  • 这听起来非常好,并且接近我想要的......可悲的是,只是 java 8......不过谢谢你的解决方案......
猜你喜欢
  • 2013-01-09
  • 2018-09-23
  • 1970-01-01
  • 2020-09-06
  • 1970-01-01
  • 2013-05-12
  • 1970-01-01
  • 1970-01-01
  • 2013-01-01
相关资源
最近更新 更多