Java 奇怪的警告 "suspicious toArray() call"

Java strange warning "suspicious toArray() call"

使用委托流实现 Stream-class 时,IntelliJ 发出奇怪的警告:

Array of type java.lang.Object[] expected, A[] found

触发警告的代码:

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*
}

完整警告:

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]);
}

我有点假设这是一个误报,由类型擦除引起,主要是因为我找不到任何理智的原因为什么这会导致问题,也因为示例来自警告与我的代码不太匹配。

但是,仔细想想,我想:如果这是因为类型擦除而触发的误报,为什么它会知道A呢?此外,Java 通常会隐式地将 A[] 转换为 Object[],那么这里为什么不呢?

所以:这里发生了什么?

IntelliJ 认为这是可疑的,因为您的流是 T 的流,但您正在生成一个 A 的数组。注意AtoArray方法的类型参数,它独立于class的类型参数T。与类型擦除关系不大。

这确实是误报。您正在实现 Stream 的方法,它最终需要返回 A[]Stream中的方法确实是这样设计的,比较不安全,允许你为A传入anything,不管它是什么类型的流。据记载,如果 A 是错误的类型,它将抛出 ArrayStoreException

Throws

ArrayStoreException - if the runtime type of the array returned from the array generator is not a supertype of the runtime type of every element in this stream

通过委派给 delegate,您将抛出一个 ArrayStoreException 您应该 ,(根据文档)如果有人传递了一个错误AtoArray,假设存储在 delegate 中的实例已正确实现。这里没什么问题

您可以通过在其上方写下此注释来抑制对这一行的检查:

//noinspection SuspiciousToArrayCall

这里有一些不同的代码可以用来说明您的 IDE 检测到的问题:

Stream<Integer> intStream = Stream.of(1, 2, 3);
String[] stringArray = intStream.toArray(i -> new String[3]);

或者,使用你自己的 class(我没有测试这个):

new MyStream<>(Stream.of(1, 2, 3)).toArray(i -> new String[3]);

该代码在第二行抛出一个 java.lang.ArrayStoreException(预计第二行也会抛出)。但它编译。

这正是 delegate.toArray(generator); 的问题所在。您实际上是在调用

Stream<T>.toArray(IntFunction<A[]>)

其中 A 不保证与 T 相同或兼容。将此与上面的示例代码联系起来,T 代替了 IntegerA 代替了 String。看到问题了吗?

Stream.toArray 的 JavaDocs 对此发出警告:

Throws: ArrayStoreException - if the runtime type of any element of this stream is not assignable to the runtime component type of the generated array


这是 IntelliJ 正在检测的问题。您不能假设 <A> 和类型范围参数 <T> 会重合。

换句话说,这不是误报,您可以使用上面的示例重现它。