我有以下课程,该课程用于在我的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实例上调用静态方法”。类的实例?” 。
换句话说-如果有人偶然尝试在实例上调用静态方法,有什么方法可以防止编译器编译?
答案 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)
我看到了几种方法可以做到这一点:
使用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()
即可避免混乱。
使用Kotlin
Kotlin实际上使在动态接收器上调用静态方法真的非常困难(如果不是不可能的话)。它不会显示在方法建议中,如果您手动键入,它将以红色下划线显示。尽管我还没走那么远,它甚至可能没有编译。
Kotlin还具有内置的null保护:instance?.method()
。 ?
意味着method()
仅在instance
为空时才会执行。
仅不要在动态接收器上调用静态方法。如果您不小心这样做,请返回并修复它。您不应该依赖Java来解决语法错误。
最后,为什么还要这样做?我高度怀疑mDevConfiguration
是否为null,除非您在一个非常奇怪的地方对其进行了初始化。如果是这样,您可能要考虑重新组织代码。同样,因为您不应该依赖Java来解决语法错误。另外,如果为null,则至少在Java中不会抛出NPE,因为它不需要运行动态接收器(在Kotlin中可能有所不同)。
由您决定使代码按其应有的方式工作,并实施适当的null检查,错误处理等。如果您错过了某些内容,那没什么大不了的;这就是为什么在发布代码之前测试代码并修复崩溃和错误的原因。您未捕获的所有内容都会由Google Play控制台(如果您在此处发布)或Firebase(如果您实施了)或您的用户进行报告。
对不起,如果以上听起来很刺耳,但我真的很难理解为什么您要这样做而不是仅仅检查代码。
如果您真的要保留此结构,请至少将DevConfiguration
的构造函数设为私有:
public class DevConfiguration {
//...
private DevConfiguration() {}
//...
}
那样,只有其中的静态方法才能创建实例。