Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. 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()).

Problem description

When i compile my code with the jdk8 it works perfectly fine. 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)

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.

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. This is fone for the compiler of jdk8. But jdk11 fails here.

    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.

    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?

like image 487
MaVo Avatar asked Jun 03 '19 09:06

MaVo


People also ask

What is the difference between Java 8 and Java 11?

There are several reasons why one should upgrade from Java 8 to Java 11. Applications written in Java 9, 10, and 11 are significantly faster and more secure than previous versions of the language. ZGC and Epsilon garbage collectors have improved Garbage Collection.

What are the advantages of Java 8 streams?

There are a lot of benefits to using streams in Java, such as the ability to write functions at a more abstract level which can reduce code bugs, compact functions into fewer and more readable lines of code, and the ease they offer for parallelization.

Does Java 8 stream improve performance?

Java 8 introduced streams. Not to be confused with input/output streams, these Java 8+ streams can also process data that goes through them. It was hailed as a great new feature that allowed coders to write algorithms in a more readable (and therefore more maintainable) way.


1 Answers

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+.

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. You are trying to rectify this later with map(ObjectId.class::cast) but it's not a good idea. Streams are heavily based on the generics and you should avoid manual casts and let the compiler infer the types.

Your code can be simplified to below, which works on 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());
like image 79
Karol Dowbecki Avatar answered Oct 19 '22 18:10

Karol Dowbecki