简体   繁体   English

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

[英]Different behaviour of jdk8 vs jdk11 when using stream collect

Intro 介绍

I do have a problem regarding the behaviour of jdk11 (and later) in terms of stream and the collect method. 关于流和collect方法,jdk11(及更高版本)的行为确实存在问题。 I do want to get the values of a parameterized container streaming the resource and collect the values in the end with .collect(Collectors.toSet()) . 我确实想要获取参数化容器的值来流式传输资源,并使用.collect(Collectors.toSet())最终的值。

Problem description 问题描述

When i compile my code with the jdk8 it works perfectly fine. 当我使用jdk8编译我的代码时,它完全正常。 But as we also have to support jdk11 , i ran the compilation and it fails because Error:(136, 17) java: incompatible types: java.lang.Object cannot be converted to java.util.Set<org.bson.types.ObjectId> (same applies for openJdk11) 但是因为我们还必须支持jdk11 ,所以我运行了编译并且它失败了因为Error:(136, 17) java: incompatible types: java.lang.Object cannot be converted to java.util.Set<org.bson.types.ObjectId> jdk11 Error:(136, 17) java: incompatible types: java.lang.Object cannot be converted to java.util.Set<org.bson.types.ObjectId> (同样适用于openJdk11)

Use case 用例

Imagine the following situation. 想象一下以下情况。 I have a class which is basically a data container. 我有一个基本上是数据容器的类。 This container can hold single values or lists of values. 此容器可以包含单个值或值列表。

Im some parts of my application, i do have lists of this container class (which can contain lists as values as well) and i do want to stream through the lists to get all the values in the containers as a flat list. 在我的应用程序的某些部分,我有这个容器类的列表(它也可以包含列表作为值),我确实希望通过列表流来获取容器中的所有值作为一个平面列表。

For this example i chose to use Lists of objectIds. 在本例中,我选择使用objectIds列表。

Set up 建立

  // 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));

Container class 集装箱类

  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;
    }
  }

Implemntation for collecting objectId value from the container. 实现从容器中收集objectId值。 This is fone for the compiler of jdk8. 这对于jdk8的编译器来说是有效的。 But jdk11 fails here. 但是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());

Changing it to this implementation makes the jdk11 compiler happy. 将其更改为此实现使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());

Question

But i do not get why this is wrong. 但我不明白这是错误的原因。

<deleted as of to be inacurate>

EDIT : I changed the set up code to reflect my current issue a bit more. 编辑 :我更改了设置代码,以反映我当前的问题。

Anyone an idea what i am doing wrong? 谁知道我做错了什么?

This could be related to JDK-8199234 Code compiles in java8 but not in java9 : "incompatible types: java.lang.Object cannot be converted ..." which was resolved as "Not an Issue" and affects Java 9+. 这可能与JDK-8199234 java8中的代码编译有关,但java9中没有:“不兼容的类型:java.lang.Object无法转换...” ,它被解析为“非问题”并影响Java 9+。

The root cause is that in your example map(List.class::cast) performs a cast to a raw type List messing up the information about generics. 根本原因是在您的示例map(List.class::cast)执行转换为原始类型List搞乱有关泛型的信息。 You are trying to rectify this later with map(ObjectId.class::cast) but it's not a good idea. 你试图用map(ObjectId.class::cast)来解决这个问题,但这不是一个好主意。 Streams are heavily based on the generics and you should avoid manual casts and let the compiler infer the types. Streams很大程度上基于泛型,你应该避免手动转换,让编译器推断出类型。

Your code can be simplified to below, which works on Java 11: 您的代码可以简化为以下,适用于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());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM