Reputation: 503
While updating my Eclipse IDE installation from Oxygen.3a (4.7.3) to Photon I have noticed that some of my code has broken despite compiling (and working) fine previously.
Consider the following code:
import java.util.ArrayList;
import java.util.stream.Stream;
public class Test {
class MyClass {
}
interface MyInterface {
}
public static void main(String[] args) {
Stream<MyClass> myClassStream = new ArrayList<MyClass>().stream();
Stream<MyInterface> myInterfaceStream = new ArrayList<MyInterface>().stream();
Stream<MyInterface> concatenation = Stream.concat(
// Tooltip for filter() displays the result type of Stream<MyClass>
myClassStream.filter(element -> element instanceof MyInterface),
// Tooltip displays type Stream<MyInterface>
myInterfaceStream);
}
}
In Photon, an error appears saying that Stream.concat
returns Stream<Object>
, not Stream<MyInterface>
. In Oxygen, it did not (the return type was Stream<MyInterface>
). It looks like something was implicitly casting the return type of filter
to Stream<? extends MyClass, MyInterface>
which then lead to Stream.concat
returning the expected type. This was, of course, safe semantically as all the elements in the stream returned by filter
did implement MyInterface
.
Why did this code break? How could I get previous behavior?
Upvotes: 3
Views: 326
Reputation: 17363
Summary:
concat()
will return a Stream of a type which is the closest match for the types of the streams being concatenated. For example, if you concat()
a Stream<Int>
with a Stream<Long>
then concat()
will return a Stream<Number>
.
And if you concat()
two streams whose elements bear no relationship with each other, such as Stream<String>
and Stream<Long>
, then concat()
will return a Stream<Object>
. That is what happens in your code, since MyClass
and MyInterface
bear no relationship to each other, apart from having Object
as a parent.
It looks like something was implicitly casting the return type of filter to Stream which then lead to Stream.concat returning the expected type.
That explanation looks tempting since it appears to fit the facts, but what happens if the Predicate in your filter is changed to test for Runnable
instead of MyInterface
?
Stream<MyInterface> concatenation2 = Stream.concat(
myClassStream.filter(element -> element instanceof Runnable),
myInterfaceStream);
The code still compiles and the tooltip is unchanged, so the filtering is clearly not impacting the return type. Put another way, the first parameter to concat()
will return a Stream<MyClass>
regardless of what is done in filter()
. The fact that your filter guaranteed that it would also only return MyInterface
elements is not relevant, but it appeared to be significant because the type of the variable receiving the Stream from concat()
was Stream<MyInterface>
.
And what happens if the type of the variable receiving the Stream from concat()
is changed from MyInterface
to something absurd and meaningless such as Deque<LocalDateTime>
?...
Stream<Deque<LocalDateTime>> concatenation3 = Stream.concat(
myClassStream.filter(element -> element instanceof MyInterface),
myInterfaceStream);
Two things are notable:
concat()
now shows its return type to be Stream<Deque<LocalDateTime>>
which clearly makes no sense. Why did this code break? How could I get previous behavior?
Oxygen's compiler was allowing invalid variable definitions using Stream.concat()
, and it seems that this was fixed in Photon. You shouldn't want that previous behavior since it was incorrect.
Upvotes: 1