(对于背景,我读过You think you know everything about CDI events… Think again!,所以我至少大部分都熟悉CDI事件中的许多边缘情况。)
我使用Weld 3.0.4.Final作为CDI 2.0的实现。
我有一个AbstractFoo<? extends T>
实例,我想将其作为CDI事件触发。我们称之为有效负载。这就是我所了解的有效载荷。
T
定义为T extends MetaData
。 MetaData
是一个界面。
由于各种不重要的原因,我必须以编程方式触发有效负载,所以我这样做:
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
- 或者 - 通过扩展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);
}
}
仅调用第一个观察者方法。我想了解是什么阻止了第二个被调用。我明白,一个未知类型 - 那是一个 - Number
不能分配给一个未知类型 - 那是一个 - Integer
,但我想要一些方法来选择有效载荷的“正确”观察者方法。
答案 0 :(得分:2)
这是在10.3.1. Assignability of type variables, raw and parameterized types的规范中定义的。更具体地说,看看你的测试用例,你会碰到这个规范句子:
- 观察到的事件类型参数是具有与事件类型参数相同的原始类型的实际类型,并且,如果类型是参数化的,则事件类型参数可根据这些规则分配给观察到的事件类型参数,或者
事件类型参数(? extends Number
)不能分配给观察到的事件类型参数(在您的测试用例中为? extends Integer
)。你的第三个观察者(Collection<Integer>
)的情况是一样的。
Collection<Number>
的最后一个案例看起来有点令人费解,但它遵循同样的规则。您无法将<? extends Number>
分配给<Number>
,因为在这种情况下您可能会有效地尝试分配Collection<Integer>
或Collection<Double>
或Collection<Number>
(<? extends Number>
可以是这些中的任何一个。)
Oracle site上有一个很好的泛型示例。它表明,给定两种类型Box<A>
和Box<B>
,你不能假设基于&#34;内部&#34;的关系的可转让性。类型(A
和B
)。