为什么枚举元素的成员在某些设备中可以为null

时间:2015-05-06 04:53:33

标签: java android

我无法在我身边产生空指针异常。但是,我确实从用户那里收到了很多空指针异常崩溃报告。

public static Currency getStockCurrency(PortfolioRealTimeInfo portfolioRealTimeInfo, Code code) {
    //////////////////////////////////////////
    // Get traded currency in this stock code.
    //////////////////////////////////////////
    final Currency stockCurrency;
    org.yccheok.jstock.engine.currency.Currency c = portfolioRealTimeInfo.currencies.get(code);
    if (c == null) {
        Country stockCountry = org.yccheok.jstock.engine.Utils.toCountry(code);
        stockCurrency = stockCountry.stockCurrency;
    } else {
        stockCurrency = c;
    }
    return stockCurrency;
}

public static double getExchangeRate(PortfolioRealTimeInfo portfolioRealTimeInfo, Currency localCurrency, Code code) {
    final Currency stockCurrency = getStockCurrency(portfolioRealTimeInfo, code);

    return getExchangeRate(portfolioRealTimeInfo, localCurrency, stockCurrency);
}

public static double getExchangeRate(PortfolioRealTimeInfo portfolioRealTimeInfo, Currency localCurrency, Currency stockCurrency) {
    // Possible null.
    if (localCurrency == null) {
        if (stockCurrency.toString().equals("GBX")) {

NPE发生在

if (stockCurrency.toString().equals("GBX")) {

然而,我不明白

  1. stockCurrency永远不能为空,因为
  2. getStockCurrency永远不会返回null,因为
  3. stockCurrency = stockCountry.stockCurrency;stockCountry不为null,因为此时不会发生崩溃。 stockCountry的成员不能为null,因为
  4. stockCountry是一个枚举。我认为没有任何理由可以stockCurrency为空:https://github.com/yccheok/jstock/blob/master/src/org/yccheok/jstock/engine/Country.java#L29
  5. Currency.valueOf永远不会返回null。它可能会抛出一些异常。但是,我从未收到过这样的例外:https://github.com/yccheok/jstock/blob/master/src/org/yccheok/jstock/engine/currency/Currency.java#L52
  6. 任何想法为什么NPE发生在行

    if (stockCurrency.toString().equals("GBX")) {
    

    更新

    使用Android 5.0的某些设备可提供更好的异常信息

    java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
    at org.yccheok.jstock.portfolio.m.a(SourceFile:244)
    at org.yccheok.jstock.portfolio.m.a(SourceFile:272)
    at org.yccheok.jstock.gui.portfolio.BuyPortfolioFragment.b(SourceFile:206)
    at org.yccheok.jstock.gui.portfolio.BuyPortfolioFragment.b(SourceFile:314)
    at org.yccheok.jstock.gui.portfolio.BuyPortfolioFragment.h(SourceFile:841)
    at org.yccheok.jstock.gui.portfolio.BuyPortfolioFragment.a(SourceFile:793)
    at org.yccheok.jstock.gui.portfolio.fd.a(SourceFile:521)
    at org.yccheok.jstock.gui.portfolio.fd.onLoadFinished(SourceFile:488)
    at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(SourceFile:427)
    at android.support.v4.app.LoaderManagerImpl$LoaderInfo.onLoadComplete(SourceFile:395)
    at android.support.v4.a.o.b(SourceFile:104)
    at android.support.v4.a.a.b(SourceFile:223)
    at android.support.v4.a.b.a(SourceFile:61)
    at android.support.v4.a.q.e(SourceFile:461)
    at android.support.v4.a.q.c(SourceFile:47)
    at android.support.v4.a.w.handleMessage(SourceFile:474)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:5944)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
    

    似乎stockCurrency.toString()已成功执行,但它返回null。但是,对我来说似乎很奇怪,因为stockCurrency.toString()不应该假设返回null。 (这是因为Currency的{​​{1}}工厂方法不接受空值)

    p / s我正在使用ProGuard。但是,我无法想象它是如何导致这次神秘崩溃的。

    p / s / s该应用可能会将我的自定义valueOf混淆为标准Currency

3 个答案:

答案 0 :(得分:1)

这个问题的原因是,Currency对象的创建不是通过普通的构造函数或工厂方法完成的。

这是通过Gson序列化完成的。

这是导致这种奇怪行为的因素

  1. 旧版json字符串是从混淆的Currency类生成的。
  2. 在新版本中,我们决定不混淆Currency类。
  3. 因此,如果我们通过以下字符串

    PortfolioRealTimeInfo执行反序列化
    String data = "{\"currencies\":[[{\"code\":\"4677.KL\"},{\"a\":\"MYR\"}],[{\"code\":\"5247.KL\"},{\"a\":\"MYR\"}],[{\"code\":\"5183.KL\"},{\"a\":\"MYR\"}],[{\"code\":\"6947.KL\"},{\"a\":\"MYR\"}],[{\"code\":\"6742.KL\"},{\"a\":\"MYR\"}]],\"stockPrices\":[[{\"code\":\"4677.KL\"},1.67],[{\"code\":\"5247.KL\"},3.18],[{\"code\":\"6947.KL\"},6.04]],\"exchangeRates\":{},\"exchangeRatesTimestamp\":0,\"stockPricesTimestamp\":1430906678615}";
    

    我们会失败。

    请注意,对于{\"a\":\"MYR\"}a是对Currency currency成员进行模糊处理的结果。

    当我们决定不混淆Currency时,Gson会帮助"帮助"我们创建一个非空Currency对象,其成员变量currency为空。

答案 1 :(得分:0)

这是一个疯狂的猜测,但我认为这是因为在这一行:

UnitedKingdom("/images/16x16/gb.png", "United Kingdom", Currency.newInstance("GBX"), Currency.newInstance("GBP")),

这应该是

UnitedKingdom("/images/16x16/gb.png", "United Kingdom", Currency.newInstance("GBP"), Currency.newInstance("GBP")),

可能是这个

if (stockCurrency.toString().equals("GBX")) {

应该是这个

if (stockCurrency.toString().equals("GBP")) {

但即使不是,我认为您应该为货币设置enum,并手动指定它们(例如在static {}初始化程序块中,而不是使用反射。

答案 2 :(得分:0)

stockCurrency.toString()方法返回的字符串返回null,例如尝试添加if条件以确保它不为null,或者如果您正在使用某些日志记录机制尝试记录应该应该的字符串值在返回之前由toString()方法返回,你会发现它返回null。然后从那里开始调试。