使用流收集时jdk8与jdk11的不同行为

时间:2019-06-03 09:58:56

标签: java java-stream collectors

简介

关于流和collect方法,jdk11(及更高版本)的行为确实存在问题。 我确实想获取流化资源的参数化容器的值,并以.collect(Collectors.toSet())最后收集值。

问题描述

当我使用jdk8编译代码时,它可以很好地工作。但是由于我们还必须支持jdk11,所以我运行了编译,但由于Error:(136, 17) java: incompatible types: java.lang.Object cannot be converted to java.util.Set<org.bson.types.ObjectId>(对于openJdk11同样适用)而失败了

用例

想象一下以下情况。我有一个基本上是数据容器的类。该容器可以容纳单个值或值列表。

在我的应用程序的某些部分中,我确实有此容器类的列表(它也可以包含列表作为值),并且我确实希望通过列表进行流传输,以将容器中的所有值作为平面列表获得。

在此示例中,我选择使用objectIds列表。

设置

  // preparation
  List<ObjectId> innerObjects = new ArrayList<>();
  innerObjects.add(new ObjectId());
  innerObjects.add(new ObjectId());

  List<Diamond<Object>> diamonds = new ArrayList<>();
  diamonds.add(new Diamond<Object>().value(innerObjects));

容器类

  public static class Diamond<T> {
    private T value;

    public Diamond<T> value(T value) {
      this.value = value;
      return this;
    }

    public T getValue() {
      return this.value;
    }
  }

用于从容器中收集objectId值的示例。对于jdk8的编译器而言,这是不理想的。但是jdk11在这里失败。

    Set<ObjectId> objectIdSet = diamonds
        .stream()
        .filter(diamond -> diamond.getValue() instanceof List)
        .map(Diamond::getValue)
        .map(List.class::cast)
        .flatMap(Collection::stream)
        .map(ObjectId.class::cast)
        .collect(Collectors.toSet());

将其更改为该实现可使jdk11编译器满意。

    Stream<ObjectId> idStream = diamonds
        .stream()
        .filter(diamond -> diamond.getValue() instanceof List)
        .map(Diamond::getValue)
        .map(List.class::cast)
        .flatMap(Collection::stream)
        .map(ObjectId.class::cast);
    Set<ObjectId> objectIds = idStream.collect(Collectors.toSet());

问题

但是我不明白为什么这是错误的。

<deleted as of to be inacurate>

编辑: 我更改了设置代码,以反映出我当前的问题。

有人知道我在做什么错吗?

1 个答案:

答案 0 :(得分:4)

这可能与JDK-8199234 Code compiles in java8 but not in java9 : "incompatible types: java.lang.Object cannot be converted ..."有关,该问题已解决为“不是问题”并影响Java 9 +。

根本原因是,在您的示例中,map(List.class::cast)对原始类型List进行了强制转换,弄乱了有关泛型的信息。您稍后尝试使用map(ObjectId.class::cast)纠正此问题,但这不是一个好主意。流在很大程度上基于泛型,因此应避免进行手动强制转换,并让编译器推断类型。

您的代码可以简化为以下代码,可在Java 11上运行:

Set<ObjectId> objectIdSet = diamonds.stream()
        .filter(Objects::nonNull) // potentially redundant but instanceof was doing it
        .map(Diamond::getValue)
        .flatMap(Collection::stream)
        .collect(Collectors.toSet());