When implementing a Stream
-class using a delegate stream, I get strange warning by IntelliJ:
Array of type
java.lang.Object[]
expected,A[]
found
The code which triggers the warning:
public class MyStream<T> implements Stream<T> {
private final Stream<T> delegate;
public MyStream(Stream<T> delegate) {
this.delegate = delegate;
}
*snip*
@Override
public <A> A[] toArray(IntFunction<A[]> generator) {
return delegate.toArray(generator);
}
*snip*
}
The full warning:
Array of type
java.lang.Object[]
expected,A[]
foundInspection info: Reports two types of suspicious calls to
Collection.toArray()
. The first type is any calls where the type of the specified array argument is not of the same type as the array type to which the result is casted. Example:void m(List list) { Number[] ns = (Number[]) list.toArray(new String[0]); }
The second type is any calls where the type of the specified array argument does not match the type parameter of the collection declaration. Example:
void m(List<Number> list) { Number[] ns = list.toArray(new String[0]); }
I somewhat assume this is a false positive, caused by type erasure, mostly because I cannot find any sane reason why this would cause a problem, and also because the examples from the warning do not match very well with my code.
However, when thinking about it, I wondered: If this is a false positive triggered because of type erasure, why does it know about A
at all? Also, Java does usually cast a A[]
to a Object[]
implicitly, so why doesn't it here?
So: What is happening here?
Here's some different code that can serve to illustrate the problem your IDE is detecting:
Stream<Integer> intStream = Stream.of(1, 2, 3);
String[] stringArray = intStream.toArray(i -> new String[3]);
Or, using your own class (I didn't test this):
new MyStream<>(Stream.of(1, 2, 3)).toArray(i -> new String[3]);
That code throws a java.lang.ArrayStoreException
on the second line (the second one would be expected to, as well). But it compiles.
That is exactly the problem with delegate.toArray(generator);
. You're effectively calling
Stream<T>.toArray(IntFunction<A[]>)
Where A
is not guaranteed to be the same as or compatible with T
. To relate this to the example code above, T
has the place of Integer
and A
has the place of String
. See the problem?
The JavaDocs of Stream.toArray
warns about this:
Throws:
ArrayStoreException
- if the runtime type of any element of this stream is not assignable to the runtime component type of the generated array
That is the problem that IntelliJ is detecting. You cannot assume that <A>
and the type-wide parameter <T>
are going to coincide.
In other words, it's not a false positive and you can use the example above to reproduce it.
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