如何防止在类的实例上调用静态方法?

时间:2018-09-26 18:24:10

标签: java android nullpointerexception static static-methods

我有以下课程,该课程用于在我的Android应用中的各个位置控制一些调试和Beta测试选项。它仅包含一些标志和一些逻辑以将其与JSON(反序列化)。

public class DevConfiguration {
    public boolean dontSendSMS;


    public static String toJsonString(DevConfiguration devConfiguration) {
        JSONObject json = new JSONObject();

        if( devConfiguration != null ) {
            try {
                json.put("dontSendSMS", devConfiguration.dontSendSMS);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        return json.toString();
    }


    public static DevConfiguration fromJsonString(String jsonString) {
        if( jsonString.isEmpty() )
            return null;

        DevConfiguration result = new DevConfiguration();

        try {
            JSONObject jsonObj = new JSONObject(jsonString);
            result.dontSendSMS = jsonObj.optBoolean("dontSendSMS", false);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return result;
    }

}

现在,在我的一项服务中,我在DevConfiguration中收到了一个序列化的Intent对象,以后可能会将其传递给另一个服务:

serviceIntent.putExtra("dev_conf", DevConfiguration.toJsonString(mDevConfiguration));

我选择使toJsonString()方法为静态,这样就不会冒着在null实例上调用它的风险。但是,仍然有可能在某处出错并在实例(可能是null实例)上调用静态方法!

mDevConfiguration.toJsonString(mDevConfiguration);

Android Studio中有Lint警告,但仍然是潜在的NullPointerException错误等待发生。我认为可以通过定义类似的private方法但使用不同的签名来隐藏它

/** Hide instance implementation **/
private String toJsonString(Object o){ return ""; }

但是,当然,使用DevConfiguration参数调用它会调用静态方法,并且IDE不会给出比以前更多的警告。

有什么方法可以从实例变量中“隐藏”静态方法吗?

编辑

注释清楚表明,在null实例上调用静态方法是完全合法的。但是,问题不是“在空实例上调用静态方法时如何防止NullPointerException?” ,而是更通用的“如何防止在null实例上调用静态方法”。类的实例?”

换句话说-如果有人偶然尝试在实例上调用静态方法,有什么方法可以防止编译器编译?

2 个答案:

答案 0 :(得分:2)

对具有空值的变量调用静态方法不会引发NullPointerException。即使变量42为空,以下代码也会打印i

public class Test {
    public static void main(String... args) {
        Integer i = null;
        System.out.println(i.parseInt("42"));
    }
}

当通过变量调用静态方法时,真正重要的是变量的声明类型而不是变量值的引用类型。这与Java中的静态方法不是多态的这一事实有关。


„如何防止在类的实例上调用静态方法?”

通过变量调用静态方法只是Java规范中定义的常规语言功能。如果有任何方法可以抑制它,我会感到惊讶。

如果必须对选定的类执行此操作,则可能会将静态方法迁移到单独的“ companion”实用程序(如另一个答案中所述)。

但是在您的类中拥有这样的静态(工厂)方法是一个很好的习惯用法(例如,参见Joshua Bloch,“有效的Java”,第1项:考虑使用静态工厂方法而不是构造函数)。我不会轻易放弃它。

答案 1 :(得分:0)

我看到了几种方法可以做到这一点:

  1. 使用Utils类:

    public class Utils {
        public static String toJsonString(DevConfiguration devConfiguration) {
            JSONObject json = new JSONObject();
    
            if( devConfiguration != null ) {
                try {
                    json.put("dontSendSMS", devConfiguration.dontSendSMS);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
    
            return json.toString();
        }
    
    
        public static DevConfiguration fromJsonString(String jsonString) {
            if( jsonString.isEmpty() )
                return null;
    
            DevConfiguration result = new DevConfiguration();
    
            try {
                JSONObject jsonObj = new JSONObject(jsonString);
                result.dontSendSMS = jsonObj.optBoolean("dontSendSMS", false);
            } catch (JSONException e) {
                e.printStackTrace();
            }
    
            return result;
        }
    }
    

    现在,您只需拨打Utils.method()即可避免混乱。

  2. 使用Kotlin

    Kotlin实际上使在动态接收器上调用静态方法真的非常困难(如果不是不可能的话)。它不会显示在方法建议中,如果您手动键入,它将以红色下划线显示。尽管我还没走那么远,它甚至可能没有编译。

    Kotlin还具有内置的null保护:instance?.method()?意味着method()仅在instance为空时才会执行。

  3. 仅不要在动态接收器上调用静态方法。如果您不小心这样做,请返回并修复它。您不应该依赖Java来解决语法错误。

最后,为什么还要这样做?我高度怀疑mDevConfiguration是否为null,除非您在一个非常奇怪的地方对其进行了初始化。如果是这样,您可能要考虑重新组织代码。同样,因为您不应该依赖Java来解决语法错误。另外,如果为null,则至少在Java中不会抛出NPE,因为它不需要运行动态接收器(在Kotlin中可能有所不同)。

由您决定使代码按其应有的方式工作,并实施适当的null检查,错误处理等。如果您错过了某些内容,那没什么大不了的;这就是为什么在发布代码之前测试代码并修复崩溃和错误的原因。您未捕获的所有内容都会由Google Play控制台(如果您在此处发布)或Firebase(如果您实施了)或您的用户进行报告。

对不起,如果以上听起来很刺耳,但我真的很难理解为什么您要这样做而不是仅仅检查代码。

如果您真的要保留此结构,请至少将DevConfiguration的构造函数设为私有:

public class DevConfiguration {
    //...

    private DevConfiguration() {}

    //...
}

那样,只有其中的静态方法才能创建实例。