消费者数组中的 ArrayStoreException<X>

ArrayStoreException in array of Consumers<X>

我正在存储对 BiConsumers<Integer, X> 的引用以适应 Consumer<Integer>:

public void setConsumer(BiConsumer<Integer, X> consumer) {
    fieldConsumer = integer -> consumer.accept(integer, fieldSubject);
}

但是我需要其中的 2 个,所以我更改了代码以使用数组:

private Consumer<Integer>[] fieldConsumers;

public MyClass(int numberOfConsumers) {
    Consumer<Integer> consumer = integer -> {};
    fieldConsumers= (Consumer<Integer>[]) Array.newInstance(consumer.getClass(), numberOfObservers);
}

public void addConsumer(int consumerIndex, BiConsumer<Integer, X> consumer) {
    // Offending line
    fieldConsumers[consumerIndex] = responseType-> consumer.accept(responseType, fieldSubject);

}

以便可以通过以下方式触发回调:

for (Consumer<Integer> consumer: fieldConsumers) {
    consumer.accept(responseType);
}

我收到错误:

java.lang.ArrayStoreException:

这一行:

fieldConsumers[consumerIndex] = responseType-> consumer.accept(responseType, fieldSubject);

现在,如果你还在看这篇文章,我还有一个问题:

如果我这样做,我是否仍然引用外部消费者,而不是使用旧的 fieldConsumers.add(consumer),其中 fieldConsumers 是 List<BiConsumer<Integer, X>>

您使用 Array.newInstance(consumer.getClass(), numberOfObservers) 创建了 Consumer<Integer>[] 数组。但是 consumer.getClass() returns 您正在调用该方法的对象的实际 class,它始终是 实现 class界面。这种元素类型的数组只能容纳相同具体的对象 class,不能容纳接口的任意实现。

这与例如

没有什么不同
CharSequence cs = "hello";
CharSequence[] array = (CharSequence[]) Array.newInstance(cs.getClass(), 1);
array[0] = new StringBuilder();

此处,cs 的类型为 CharSequence,反射数组创建似乎创建了一个类型为 CharSequence[] 的数组,因此存储 StringBuilder 应该是可能的。但是由于 cs.getClass() returns 实际实现 class String,数组实际上是 String[] 类型,因此,尝试存储 StringBuilder产生一个 ArrayStoreException.

在 lambda 表达式的情况下,事情会稍微复杂一些,因为功能接口的实际实现 classes 在运行时提供并且有意未指定。您在构造函数中使用了 lambda 表达式 integer -> {} 来创建数组,它的计算结果 class 与 addConsumer 方法中的 responseType-> consumer.accept(responseType, fieldSubject) 不同在这个特定的运行时.

此行为符合 this answer 描述最常用环境的行为。尽管如此,其他实现可能会表现出不同的行为,例如对于所有 lambda 表达式的特定功能接口,评估为相同的实现 class。但也有可能对同一个 lambda 表达式的多次求值产生不同的 classes.

所以解决方法是使用预期的界面元素类型,例如

fieldConsumers=(Consumer<Integer>[])Array.newInstance(Consumer.class, numberOfObservers);

但是根本不需要创建反射数组。您可以使用:

fieldConsumers = new Consumer[numberOfObservers];

您不能写 new Consumer<Integer>[numberOfObservers],因为不允许创建通用数组。这就是为什么上面的代码使用原始类型。使用反射不会改善这种情况,因为在任何一种情况下它都是未经检查的操作。您可能需要为其添加 @SuppressWarnings。更简洁的替代方法是使用 List<Consumer<Integer>>,因为它使您免受数组和泛型的怪异影响。

不清楚您在这里所说的“参考外部消费者”是什么意思。在任何一种情况下,您都有对 Consumer 实现的引用捕获对 BiConsumer 实现的引用,这些实现是作为 addConsumer.

的参数收到的