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

MaV*_*aVo 9 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));
Run Code Online (Sandbox Code Playgroud)

容器类

  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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

从容器收集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());
Run Code Online (Sandbox Code Playgroud)

将其更改为该实现将使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());
Run Code Online (Sandbox Code Playgroud)

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

<deleted as of to be inacurate>

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

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

Kar*_*cki 4

这可能与JDK-8199234 代码在 java8 中编译但不在 java9 中相关:“不兼容的类型:java.lang.Object 无法转换...”,该问题被解析为“不是问题”并影响 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());
Run Code Online (Sandbox Code Playgroud)

  • 虽然这都是真的,但我认为问题是*为什么编译器给出错误*?或者至少,回答这个问题会很有趣。 (4认同)
  • 更详细地说,“.map(List.class::cast)”为您提供一个“Stream&lt;List&gt;”,然后“.flatMap(Collection::stream)”为您提供一个“Stream”,然后“.map”原始 `Stream` 上的 (ObjectId.class::cast)` 类似于 `map(Function)`,仍然生成原始 `Stream`,原始 `Stream` 上的 `collect` 返回 `Object`,即之后的返回类型类型擦除。将 `.map(List.class::cast)` 替换为 `.map(obj -&gt; (List&lt;?&gt;)obj)`,以避免原始类型。 (4认同)