【问题标题】:Different behaviour of jdk8 vs jdk11 when using stream collect使用流收集时 jdk8 与 jdk11 的不同行为
【发布时间】:2019-06-03 09:58:56
【问题描述】:

简介

我确实对 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());

问题

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

&lt;deleted as of to be inacurate&gt;

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

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

【问题讨论】:

  • 我不明白你的最后一段。你说在你的真实代码中你使用Diamond&lt;Object&gt;,但你在这里使用了不同的类型。 两种这两种情况是否会引发该异常,还是只有您的 Diamond&lt;Object&gt; 版本失败?
  • 我不知道您的问题的答案,但也许您应该考虑始终持有一组值 (ObjectIds)。是的,可能大部分集合都是Collections.singletonList(value),但这很好。单个项目的集合仍然是一个很好的集合。
  • @GiacomoAlzetta 是的,我的真实代码使用Diamond&lt;Object&gt;。我尝试了更明确的一个,但都失败了。我会调整我的问题更准确。
  • 虽然我没能重现 Java-12 中的编译器问题,但我很好奇你为什么倾向于使用List&lt;Diamond&lt;Object&gt;&gt; diamonds = new ArrayList&lt;&gt;(); diamonds.add(new Diamond&lt;Object&gt;().value(innerObjects));

标签: java java-stream collectors


【解决方案1】:

这可能与 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());

【讨论】:

  • 虽然这都是真的,但我认为问题是为什么编译器会出错?或者至少,回答这个问题会很有趣..
  • @YassinHajaj 这是真的。另外我的问题是,正如我所写但可能不存在,我的容器被键入为Diamond&lt;Object&gt;,这使得.flatMap(Collection::stream) 无法编译。
  • @MaVo 你的例子没有使用Diamond&lt;Object&gt;,而是Diamond&lt;List&lt;ObjectId&gt;&gt;
  • @KarolDowbecki 我将问题调整得更精确。
  • 更详细一点,.map(List.class::cast) 给你一个Stream&lt;List&gt;,然后.flatMap(Collection::stream) 给你一个Stream,然后.map(ObjectId.class::cast) 原始Stream 就像map(Function) ,仍然在原始Stream 上产生原始Streamcollect 返回Object,类型擦除后的返回类型。将.map(List.class::cast) 替换为.map(obj -&gt; (List&lt;?&gt;)obj),以避免原始类型。
猜你喜欢
  • 2019-06-24
  • 2021-08-20
  • 2019-11-04
  • 2021-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-26
相关资源
最近更新 更多