public class Test {
static List<Object> listA = new ArrayList<>();
public static void main(final String[] args) {
final List<TestClass> listB = new ArrayList<>();
listB.add(new TestClass());
// not working
setListA(listB);
// working
setListA(listB.stream().collect(Collectors.toList()));
System.out.println();
}
private static void setListA(final List<Object> list) {
listA = list;
}
}
why does it work with streams and does not work for the simple set?
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.
For the first case, it fails because List<TestClass>
is not a subtype of List<Object>
.1
For the second case, we have the following method declarations:
interface Stream<T> {
// ...
<R, A> R collect(Collector<? super T, A, R> collector)
}
and:
class Collectors {
// ...
public static <T> Collector<T, ?, List<T>> toList()
}
This allows Java to infer the generic type parameters from the context.2 In this case List<Object>
is inferred for R
, and Object
for T
.
Thus your code is equivalent to this:
Collector<Object, ?, List<Object>> tmpCollector = Collectors.toList();
List<Object> tmpList = listB.stream().collect(tmpCollector);
setListA(tmpList);
1. See e.g. here.
2. See e.g. here or here.
This line
setListA(listB);
doesn't work because List
in Java is invariant, meaning List<TestClass>
doesn't extends List<Object>
when TestClass
extends Object
. More details here
This line
setListA(listB.stream().collect(Collectors.toList()));
works because Java infer Object for Collector's generic type from this method signature setListA(final List<Object> list)
and so you actually pass List<Object>
there
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