Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java 8 list of optionals collect to list compilation error

Tags:

java

java-8

I am having trouble understanding difference between

Stream<Optional<Integer>> optionalStream = Stream.of(
                Optional.of(1),
                Optional.empty(),
                Optional.of(5));

List<Optional<Integer>> optionalList = optionalStream.collect(Collectors.toList());

Which works just fine and :

List<Optional<Integer>> optionalList1 = Stream.of(
                Optional.of(1),
                Optional.empty(),
                Optional.of(5)).collect(Collectors.toList());

Where I receive error

Error:(138, 40) java: incompatible types: inference variable T has incompatible bounds
    equality constraints: java.util.Optional<java.lang.Integer>
    lower bounds: java.util.Optional<? extends java.lang.Object>
like image 855
Konrad Kostrzewa Avatar asked Oct 20 '25 12:10

Konrad Kostrzewa


1 Answers

I've reduced the examples a bit, and tried to compile the with -XDverboseResolution=all to output information about the type inference:

final class One {
  void one() {
    Stream<Optional<Integer>> optionalStream = Stream.of(Optional.empty());
    List<Optional<Integer>> optionalList = optionalStream.collect(Collectors.toList());
  }
}

final class Two {
  void two() {
    List<Optional<Integer>> optionalList1 =
        Stream.of(Optional.empty()).collect(Collectors.toList());
  }
}

In the case of Two, it looks like the deferred instantiation of Stream.of is done before even looking at the subsequent collect:

...
Two.java:9: Note: Deferred instantiation of method <T>of(T)
        Stream.of(Optional.empty()).collect(Collectors.toList());
                 ^
  instantiated signature: (Optional<Object>)Stream<Optional<Object>>
  target-type: <none>
  where T is a type-variable:
    T extends Object declared in method <T>of(T)
Two.java:9: Note: resolving method collect in type Stream to candidate 0
        Stream.of(Optional.empty()).collect(Collectors.toList());
...

(the "resolving method collect is the first mention of collect)

There is no target-type to constrain it; the instantiated signature shows that it's a Stream<Optional<Object>>.

If you look at the corresponding output for One:

...
One.java:8: Note: Deferred instantiation of method <T>of(T)
    Stream<Optional<Integer>> optionalStream = Stream.of(Optional.empty());
                                                        ^
  instantiated signature: (Optional<Integer>)Stream<Optional<Integer>>
  target-type: Stream<Optional<Integer>>
  where T is a type-variable:
    T extends Object declared in method <T>of(T)
...

It gets this right because it knows a target type.

I can't say exactly why the deferred instantiation occurs at this point for Two, as I'm not familiar enough with the way the type inference is applied.

I think it's because the invocation of Stream.of isn't considered a poly expression, but I can't really convince myself why (see edit history for some incoherent ramblings).


The fix I'd suggest is to apply a type hint to the Optional.empty(), i.e. Optional.<Integer>empty(). This has the effect of getting the actual type of the Optional right earlier in the inference, so it is known at the time of the deferred instantiation, event though the target type is still unknown:

final class Three {
  void three() {
    List<Optional<Integer>> optionalList1 =
        Stream.of(Optional.<Integer>empty()).collect(Collectors.toList());
  }
}

...
Three.java:9: Note: resolving method of in type Stream to candidate 1
        Stream.of(Optional.<Integer>empty()).collect(Collectors.toList());
              ^
  phase: BASIC
  with actuals: Optional<Integer>
  with type-args: no arguments
  candidates:
      #0 not applicable method found: <T#1>of(T#1...)
        (cannot infer type-variable(s) T#1
          (argument mismatch; Optional<Integer> cannot be converted to T#1[]))
      #1 applicable method found: <T#2>of(T#2)
        (partially instantiated to: (Optional<Integer>)Stream<Optional<Integer>>)
  where T#1,T#2 are type-variables:
    T#1 extends Object declared in method <T#1>of(T#1...)
    T#2 extends Object declared in method <T#2>of(T#2)
Three.java:9: Note: Deferred instantiation of method <T>of(T)
        Stream.of(Optional.<Integer>empty()).collect(Collectors.toList());
                 ^
  instantiated signature: (Optional<Integer>)Stream<Optional<Integer>>
  target-type: <none>
  where T is a type-variable:
    T extends Object declared in method <T>of(T)
...
like image 105
Andy Turner Avatar answered Oct 23 '25 03:10

Andy Turner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!