【问题标题】:onError java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference [duplicate]onError java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“double java.lang.Double.doubleValue()”[重复]
【发布时间】:2017-09-22 08:20:51
【问题描述】:

我编写了以下代码,用于查找特定预算内的漫画 pageCount

起初我试图提出具有这样架构的代码:

  • Stream 提供 MarvelComic 对象的价格。
  • 我将流中 MarvelComic 对象的价格与该流中之前的漫画价格相加,并检查它是否为 BUDGET
  • 如果是,那么我将 MarvelComic 对象的 pageCount 与流下来的先前 MarvelComic 对象的 pageCount 总和相加。
  • 如果是,则调用订阅者的 onNext

由于我无法设计出一种方法来编写上述步骤中提到的代码,因此我求助于将命令式编程与反应式编程相结合。结果我写了以下代码:

Observable.fromIterable(getMarvelComicsList()).
                map(new Function<MarvelComic, HashMap<String, Double>>() {
                    @Override
                    public HashMap<String, Double> apply(@NonNull MarvelComic marvelComic) throws Exception {
                        HashMap<String, Double> map = new HashMap<String, Double>();
                        map.put("price", Double.valueOf(marvelComic.getPrice()));
                        map.put("pageCount", Double.valueOf(marvelComic.getPageCount()));
                        map.put("comicCount", Double.valueOf(marvelComic.getPageCount()));
                        return map;
                    }
                })
                .scan(new HashMap<String, Double>(), new BiFunction<HashMap<String, Double>, HashMap<String, Double>, HashMap<String, Double>>() {
                    @Override
                    public HashMap<String, Double> apply(@NonNull HashMap<String, Double> inputMap, @NonNull HashMap<String, Double> newValueMap) throws Exception {
                        double sum = inputMap.get("price")+newValueMap.get("price");
                        double count = inputMap.get("pageCount")+newValueMap.get("pageCount");
                        double comicCount = inputMap.get("comicCount")+newValueMap.get("comicCount");

                        HashMap<String, Double> map = new HashMap<String, Double>();
                        map.put("price", sum);
                        map.put("pageCount", count);
                        map.put("comicCount", comicCount);

                        return map;
                    }
                })
                .takeWhile(new Predicate<HashMap<String, Double>>() {
                    @Override
                    public boolean test(@NonNull HashMap<String, Double> stringDoubleHashMap) throws Exception {
                        return stringDoubleHashMap.get("price") < budget;
                    }
                })
                .subscribe(new DisposableObserver<HashMap<String, Double>>() {
                    @Override
                    public void onNext(HashMap<String, Double> stringDoubleHashMap) {
                        double sum = stringDoubleHashMap.get("price");
                        double pageCount = stringDoubleHashMap.get("pageCount");
                        double comicCount = stringDoubleHashMap.get("comicCount");
                        Timber.e("sum %s  pageCount %s  ComicCount: %s", sum, pageCount, comicCount);
                    }

                    @Override
                    public void onError(Throwable e) {
                        Timber.e("onError %s", e.fillInStackTrace());
                    }

                    @Override
                    public void onComplete() {
                        Timber.e("onComplete");
                    }
                });

我的预订:

  1. 每次在 map(), scan() 中创建一个新的 Hashmap 是个好主意吗?
  2. 如何进一步改进此代码?

问题:

这段代码在onError 中给出NullPointerException,因为map.get("price")scan() 中返回null。我不太确定原因。

错误:

 onError java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference

注意:

HashMap 不为空,由于某种原因,双字段被返回为 NULL。我想知道怎么做。

【问题讨论】:

  • 为什么不使用具有double 字段和两个int 字段的类而不是HashMap
  • 是的,我想到了,但我认为与其创建一个单独的类,不如求助于一个集合将是一个更清洁的解决方案。即使我创建了另一个类,我也必须在mentinoed 方法中创建该类的对象
  • @DharmbirSingh 请发布问题并停止评论与此问题无关的内容。
  • 我不认为HashMap 的这种使用特别干净。如果你按照我的建议,你不会遇到空指针异常的问题。
  • 不过,就解决您当前的问题而言,您能做的最好的事情是使用调试器逐步执行此代码,以找出意外 null 的确切来源。

标签: java android rx-java observable rx-android


【解决方案1】:

这个问题很可能是你有一个空的初始地图,因为

.scan(new HashMap<String, Double>(), ...)

当第一个真正的地图从上游到达时,你正试图从那个空的初始地图中获取值:

double sum = inputMap.get("price")+newValueMap.get("price");

我假设您想通过使用 scan 来运行属性聚合,因此您应该尝试 scan(BiFunction) 按原样发出第一个上游值,然后开始将前一个值与新的上游值组合。

或者,您可以使用默认值预初始化 new HashMap&lt;&gt;() 并避免 NPE:

HashMap<String, Double> initialMap = new HashMap<String, Double>();
initialMap.put("price", 0.0d);
initialMap.put("pageCount", 0.0d);
initialMap.put("comicCount", 0.0d);

Observable.fromIterable(getMarvelComicsList()).
            map(new Function<MarvelComic, HashMap<String, Double>>() {
                @Override
                public HashMap<String, Double> apply(@NonNull MarvelComic marvelComic) {
                    HashMap<String, Double> map = new HashMap<String, Double>();
                    map.put("price", Double.valueOf(marvelComic.getPrice()));
                    map.put("pageCount", Double.valueOf(marvelComic.getPageCount()));
                    map.put("comicCount", Double.valueOf(marvelComic.getPageCount()));
                    return map;
                }
            })
            .scan(initialMap, 
            new BiFunction<HashMap<String, Double>, 
                    HashMap<String, Double>, HashMap<String, Double>>() {
                @Override
                public HashMap<String, Double> apply(
                         @NonNull HashMap<String, Double> inputMap, 
                         @NonNull HashMap<String, Double> newValueMap) {
                    double sum = inputMap.get("price")+newValueMap.get("price");
                    double count = inputMap.get("pageCount")
                        +newValueMap.get("pageCount");
                    double comicCount = inputMap.get("comicCount")
                        +newValueMap.get("comicCount");

                    HashMap<String, Double> map = new HashMap<String, Double>();
                    map.put("price", sum);
                    map.put("pageCount", count);
                    map.put("comicCount", comicCount);

                    return map;
                }
            })
            // etc.

【讨论】:

  • 非常感谢。这正是我做错了。但是你能告诉我一个人在什么情况下会使用scan(initiailvalue, new BiFunc() ...)?另一个问题是,使用集合对多个字段执行操作的方式好吗?或者我应该为所有属性编写单独的 observables 并压缩它们?我不知道那个 zip() 东西是否会起作用(仍然是新手),但 IMO 效率低下,因为我将有 N 个可观察对象,并且我将针对 N 个字段对它们进行 N 次检查。
  • 即使上游为空,您也需要该初始值。属于一起的值应该一起传播,当它们一起可用时,没有太多理由将它们分成自己的流。您还应该考虑使用适当的值类而不是 Hans 所展示的 HashMap。
  • @Thanks 我为此过滤过程创建了一个单独的模型类。我曾经并且仍然不愿意为每个流程创建单独的模型类,而不是使用集合。在我看来,额外的模型类只会产生噪音,但考虑到我的知识有限,创建模型类可能是最好的途径。
【解决方案2】:

我尝试使用不同的方法解决您的问题,该方法不会引发任何 NPE。

请不要使用 HashMaps 作为数据结构。正在发生的事情是不透明的。您应该创建有意义的类。

此外,订阅者不应执行任何业务逻辑。订阅者实际上应该只使用结果并执行诸如更改视图之类的副作用。

希望我确实正确理解了您的问题。

@Test
void name() {
    ArrayList<MarvelComic> marvelComics = Lists.newArrayList(new MarvelComic(10, 200), new MarvelComic(3, 133), new MarvelComic(5, 555), new MarvelComic(32, 392));

    final double BUDGET = 20.0;

    Observable<Result> resultObservable = Observable.fromIterable(marvelComics)
            .scan(Result.IDENTITY, (result, marvelComic) -> {
                double priceSum = result.sumPrice + marvelComic.getPrice();

                if (priceSum <= BUDGET) {
                    int pageCount = result.sumPageCount + marvelComic.getPageCount();
                    int comicCount = result.comicCount + 1;
                    return new Result(pageCount, priceSum, comicCount);
                }

                return Result.IDENTITY;
            })
            .skip(1) // because first Value would be Result.IDENTITY
            .takeWhile(result -> result != Result.IDENTITY);

    TestObserver<Result> test = resultObservable.test().assertValueCount(3);

    Result result1 = test.values()
            .stream()
            .reduce((result, result2) -> result2)
            .get();

    assertThat(result1.comicCount).isEqualTo(3);
    assertThat(result1.sumPageCount).isEqualTo(888);
    assertThat(result1.sumPrice).isEqualTo(18);
}

class MarvelComic {
    private final double price;
    private final int pageCount;

    MarvelComic(double price, int pageCount) {
        this.price = price;
        this.pageCount = pageCount;
    }

    public double getPrice() {
        return price;
    }

    public int getPageCount() {
        return pageCount;
    }
}

static class Result {
    private final int sumPageCount;

    private final double sumPrice;

    private final int comicCount;

    Result(int sumPageCount, double sumPrice, int comicCount) {
        this.sumPageCount = sumPageCount;
        this.sumPrice = sumPrice;
        this.comicCount = comicCount;
    }

    static Result IDENTITY = new Result(0, 0, 0);
}

【讨论】:

  • 您能告诉我为什么 HashMap 会返回 null 代替值,即使我自己将值放入其中并仔细检查了它?
  • 看看@akarnokd 的答案。 Scan 将空的 hashMap 推送给订阅者。如果元素不在 hashMap 中,Get 将返回 null。您尝试在返回 null 时调用方法。请注意get方法的说明: 返回:指定键映射到的值,如果此映射不包含该键的映射,则返回null
【解决方案3】:

你在这里使用了 doubleValue() 函数 3 次,

map.put("price", Double.valueOf(marvelComic.getPrice()));
map.put("pageCount", Double.valueOf(marvelComic.getPageCount()));
map.put("comicCount", Double.valueOf(marvelComic.getPageCount()));

确认 marvelComic 具有 Price And Page Count 的值, 我认为您缺少 comicCount,因为您将 pageCount 添加为 comicCount 地图中的em>

我建议使用 try catch 并打印错误以了解根本原因

【讨论】:

  • 我在问题中提到漫威漫画有价值观
  • 能发一下漫威漫画的代码吗
【解决方案4】:

我认为您可以获得 getPrice(),getPageCount() 方法的 null 或空白值

 map.put("price", Double.valueOf(marvelComic.getPrice()));
                            map.put("pageCount", Double.valueOf(marvelComic.getPageCount()));
                            map.put("comicCount", Double.valueOf(marvelComic.getPageCount()));

或者你可以使用Double.parseDouble();方法

【讨论】:

  • 否,值不为空或为空。在将它们插入哈希映射之前,我仔细检查了这些值
  • 你能试试 Double.parseDouble();方法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-21
  • 2016-07-30
  • 2015-02-09
  • 2020-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多