Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java strange warning "suspicious toArray() call"

Tags:

java

warnings

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[] found

Inspection 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?

like image 262
Qw3ry Avatar asked Oct 20 '25 03:10

Qw3ry


1 Answers

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.

like image 123
ernest_k Avatar answered Oct 21 '25 18:10

ernest_k



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!