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>
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)
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With