0

(作为背景,我读过你认为你对 CDI 事件了如指掌……再想一想!所以我至少对 CDI 事件中的许多边缘情况非常熟悉。)

我使用 Weld 3.0.4.Final 作为 CDI 2.0 的实现。

我有一个AbstractFoo<? extends T>我想作为 CDI 事件触发的实例。我们将其称为有效载荷。这就是我对有效载荷的全部了解。

T定义为T extends MetaDataMetaData是一个接口。

由于各种不重要的原因,我必须以编程方式触发有效负载,所以我这样做:

final Event<Object> cdiEventMachinery = beanManager.getEvent();
assert cdiEventMachinery != null;
final TypeLiteral<AbstractFoo<? extends T>> eventTypeLiteral = new TypeLiteral<AbstractFoo<? extends T>>() {
  private static final long serialVersionUID = 1L;
};
final Event<AbstractFoo<? extends T>> broadcaster = 
cdiEventMachinery.select(eventTypeLiteral, someQualifiers);
assert broadcaster != null;

显然,这里broadcaster现在设置为触发我的有效载荷。我的意图是这些事件应该被传递给寻找AbstractFoo由任何扩展类型参数化的 -or-its-subclasses 实例的观察者MetaData

但是当我这样做时broadcaster.fire(payload)我注意到像这样的观察者方法:

private final void onFoo(@ObservesAsync @MatchingQualifier final Foo<? extends MetaDataSubclass> event) {}

……不要被叫。 (这? extends MetaDataSubclass似乎是罪魁祸首;如果观察到的参数很简单,Object那么显然该方法会被通知。)

具体来说,假设:

  • 一个MatchingQualifier字面量出现在someQualifiers, 和
  • MetaDataSubclass是一个扩展的类MetaData,并且
  • Foo是一个扩展的类AbstractFoo

…为什么不调用观察者方法?

需要明确的是,我确信这不是错误,而是我的理解中缺少的东西。我想知道我错过了什么。

(交叉发布到developer.jboss.org。)

测试用例

这是一个使用更简单结构的测试用例。

private final void collectionExtendsNumber(@Observes final Collection<? extends Number> payload) {
  System.out.println("*** collection extends Number");
}

private final void collectionExtendsInteger(@Observes final Collection<? extends Integer> payload) {
  System.out.println("*** collection extends Integer");
}

private final void collectionInteger(@Observes final Collection<Integer> payload) {
  System.out.println("*** collection Integer");
}

private final void collectionNumber(@Observes final Collection<Number> payload) {
  System.out.println("*** collection Number");
}

@Test
public void testContainerStartup() {

  final SeContainerInitializer initializer = SeContainerInitializer.newInstance();
  initializer.disableDiscovery();
  initializer.addBeanClasses(this.getClass());
  try (final SeContainer container = initializer.initialize()) {
    assertNotNull(container);
    final BeanManager beanManager = container.getBeanManager();
    assertNotNull(beanManager);
    final TypeLiteral<Collection<? extends Number>> literal = new TypeLiteral<Collection<? extends Number>>() {
      private static final long serialVersionUID = 1L;

    };
    final Event<Collection<? extends Number>> broadcaster = beanManager.getEvent().select(literal);
    assertNotNull(broadcaster);
    final Collection<? extends Number> payload = Collections.singleton(Integer.valueOf(1));
    broadcaster.fire(payload);
  }
}

只有第一个观察者方法被调用。我想了解是什么阻止了第二个被调用。我知道 an-unknown-type-that-is-a-Number不能分配给 an-unknown-type-that-is-a- Integer,但我想要一些方法来为有效负载选择“正确”的观察者方法.

4

1 回答 1

3

这在规范中由10.3.1 定义。类型变量、原始类型和参数化类型的可分配性。更具体地说,查看您的测试用例,您会遇到以下规范语句:

  • 观察到的事件类型参数是与事件类型参数具有相同原始类型的实际类型,并且,如果类型被参数化,则事件类型参数可根据这些规则分配给观察到的事件类型参数,或

事件类型参数( ? extends Number) 不可分配给观察到的事件类型参数? extends Integer在您的测试用例中)。您的第三个观察者 ( Collection<Integer>) 的情况也是如此。

最后一种情况Collection<Number>看起来有点令人费解,但它遵循相同的规则。您不能分配给<? extends Number><Number>因为在这种情况下,您可能正在有效地尝试分配Collection<Integer>Collection<Double>Collection<Number><? extends Number>可以是其中的任何一个)。

Oracle 网站上有一个很好的泛型示例。它表明,给定两种类型Box<A>Box<B>,您不能基于“内部”类型 (AB) 的关系来假设可分配性。

于 2018-06-18T11:46:44.710 回答