Why is `Stream.collect` type-safe and `Stream.toAr

2019-02-04 08:40发布

问题:

Consider the following code fragment

String strings[] = {"test"};
final List<String> collect = java.util.Arrays.stream(strings).collect(java.util.stream.Collectors.toList());
final Double[] array = java.util.Arrays.stream(strings).toArray(Double[]::new);

Why can Java guarantee the correct type in the collect-case (changing the generic type of collect to e.g. Double leads to a compile time error), but not in the array case (compiles fine, despite apply(int) of Double[]::new gives a Double[], not an Object[], but throws ArrayStoreException if used incorrectly as above)?

What would be the best way to generate a compile time error in case I change the type of the stream without changing the given IntFunction in the toArray call?

回答1:

The signature of the method Stream::toArray looks as follows. Please note that the type parameters T and A are completely unrelated.

public interface Stream<T> {
    <A> A[] toArray(IntFunction<A[]> generator);
}

In the source of ReferencePipeline.java, you can find the following comment:

Since A has no relation to U (not possible to declare that A is an upper bound of U) there will be no static type checking. Therefore use a raw type and assume A == U rather than propagating the separation of A and U throughout the code-base. The runtime type of U is never checked for equality with the component type of the runtime type of A[]. Runtime checking will be performed when an element is stored in A[], thus if A is not a super type of U an ArrayStoreException will be thrown.