3

我有一个任务,我需要将以下 Java 8 之前的代码转换为 Java 8 代码。以下只是一种让我很难完成的方法:

  public static List<VehicleMake> loadMatching(Region region, String nameStartsWith, VehicleLoader loader) {
    if ((nameStartsWith == null) || (region == null) || (loader == null)) {
        throw new IllegalArgumentException("The VehicleLoader and both region and nameStartsWith are required when loading VehicleMake matches");
    }
    List<VehicleMake> regionMakes = loader.getVehicleMakesByRegion(region.name());
    if (regionMakes == null) {
        return null;
    }
    List<VehicleMake> matches = new ArrayList<>(regionMakes.size());
    for (VehicleMake make : regionMakes) {
        if ((make.getName() == null) || !make.getName().startsWith(nameStartsWith)) {
            continue;
        }
        matches.add(make);
    }
    return matches;
}

我想通过使用Optional<T>而不修改以前创建的类和接口来删除空检查。

我尝试通过更改方法返回类型并执行以下操作开始,但编译器抛出此错误:
Bad return type in method reference因为 VehicleMake 类没有可选的实例字段。
以下是我的代码尝试:

   public static Optional<List<VehicleMake>> loadMatchingJava8(Region region, String nameStartsWith, VehicleLoader loader) {
    Optional<List<VehicleMake>> regionMakes = Optional.ofNullable(loader).ifPresent(loader.getVehicleMakesByRegion(Optional.ofNullable(region).ifPresent(region.name())));
    /*

    TODO rest of the conversion
     */
}

编辑:通过不将参数传递给方法引用来删除flatMap和更正的代码。但现在它不让我region.name()通过getVehicleMakesByRegion()

编辑:将消费者传递给ifPresent()

Optional<List<VehicleMake>> regionMakes = Optional.ofNullable(loader).ifPresent(()-> loader.getVehicleMakesByRegion(Optional.ofNullable(region).ifPresent(()->region.name()));
4

4 回答 4

4

您可以将您的初始null支票替换为

Optional.ofNullable(nameStartsWith)
        .flatMap(x -> Optional.ofNullable(region))
        .flatMap(x -> Optional.ofNullable(loader))
        .orElseThrow(() -> new IllegalArgumentException(
            "The VehicleLoader and both region and nameStartsWith"
          + " are required when loading VehicleMake matches"));

但这是对该 API 的滥用。更糟糕的是,它浪费了资源用于在错误情况下提供相当无意义的异常的可疑目标。

与之比较

Objects.requireNonNull(region, "region is null");
Objects.requireNonNull(nameStartsWith, "nameStartsWith is null");
Objects.requireNonNull(loader, "loader is null");

这很简洁,并且会在错误情况下抛出带有精确消息的异常。它将是 aNullPointerException而不是IllegalArgumentException,但即使是这样的变化也会导致对实际问题的更精确描述。

关于方法的其余部分,我强烈建议不要让Collectionsnull放在首位。然后,您不必测试getVehicleMakesByRegionfor的结果,null也不会自行返回null

但是,如果您必须保持原始逻辑,则可以使用

return Optional.ofNullable(loader.getVehicleMakesByRegion(region.name()))
               .map(regionMakes -> regionMakes.stream()
                    .filter(make -> Optional.ofNullable(make.getName())
                                            .filter(name->name.startsWith(nameStartsWith))
                                            .isPresent())
                    .collect(Collectors.toList()))
               .orElse(null);

旨在拒绝 null引用的初始代码不应与旨在处理 null引用的实际操作混在一起。

于 2017-05-03T09:21:13.930 回答
2

我已经更新了您的代码Optional

     public static List<VehicleMake> loadMatchingJava8(Region region, String nameStartsWith, VehicleLoader loader) {
        Optional<List<VehicleMake>> regionMakes = Optional.ofNullable(region)
                .flatMap(r -> Optional.ofNullable(loader).map(l -> l.getVehicleMakesByRegion(r.name())));

        return Optional.ofNullable(nameStartsWith)
                .map(s -> regionMakes
                    .map(Collection::stream)
                    .orElse(Stream.empty())
                    .filter(make -> make.getName() != null && make.getName().startsWith(s))
                    .collect(Collectors.toList()))
                .orElse(Collections.emptyList());
    }
于 2017-05-03T05:53:28.700 回答
1

如果您真的想将流控制转换为Optional,与您保持一致的代码应该是这样的(我将代码分成两行进行打印):

public static Optional<List<VehicleMake>> loadMatchingJava8(Region region, 
                                                            String nameStartsWith,
                                                            VehicleLoader loader) {
    if ((nameStartsWith == null) || (region == null) || (loader == null)) {
        throw new IllegalArgumentException("The VehicleLoader and both region and " +
                "nameStartsWith are required when loading VehicleMake matches");
    }


    return Optional.ofNullable(loader.getVehicleMakesByRegion(region.name()))
            .map(makers -> makers.stream()
                    .filter((it) -> it.getName() != null
                            && it.getName().startsWith(nameStartsWith))
                    .collect(Collectors.toList()));
}

注意Optional:您可以在这个问题中看到更多关于为什么不滥用的信息。

于 2017-05-03T06:21:53.520 回答
0

我不能说这很优雅,但它应该满足您的要求。没有明确的空检查,但如果任何输入参数为空,它会抛出异常,并从结果列表中过滤掉名称无效的车辆。

public static List<VehicleMake> loadMatching(Region region, String nameStartsWith, VehicleLoader loader) {
    return Optional.ofNullable(nameStartsWith)
            .flatMap(startWith -> Optional.ofNullable(loader)
                    .flatMap(vl -> Optional.ofNullable(region)
                            .map(Region::name)
                            .map(vl::getVehicleMakesByRegion))
                    .map(makes -> makes.stream()
                            .filter(make -> Optional.ofNullable(make.getName())
                                    .filter(name -> name.startsWith(startWith))
                                    .isPresent())
                            .collect(Collectors.toList())))
            .orElseThrow(() -> new IllegalArgumentException("The VehicleLoader and both region and nameStartsWith are required when loading VehicleMake matches"));
于 2017-05-03T07:25:23.537 回答