Можете ли вы запускать события CDI 2.0 типа AbstractClass<? Что-то расширяет>?
(Для справки я прочитал. Вы думаете, что знаете все о событиях CDI... Подумайте еще раз!, Так что я, по крайней мере, в основном знаком со многими крайними случаями в событиях 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) {}
… Не звони. (The ? extends MetaDataSubclass
кажется виновником; если наблюдаемый параметр просто, скажем, Object
тогда, очевидно, метод уведомляется.)
В частности, при условии, что:
MatchingQualifier
буквальный присутствует вsomeQualifiers
, а такжеMetaDataSubclass
это класс, который расширяетсяMetaData
, а такжеFoo
это класс, который расширяетсяAbstractFoo
... почему этот метод наблюдателя не вызывается?
Чтобы быть ясным, я уверен, что это не ошибка, но чего-то не хватает в моем понимании. Я хотел бы узнать, что мне не хватает.
(Перемещено на https://developer.jboss.org/message/983438.)
Прецедент
Вот тестовый пример с использованием более простых конструкций.
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
, но я бы хотел как-то выбрать "правильный" метод наблюдателя для полезной нагрузки.
1 ответ
Это определено в спецификации в 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>
Вы не можете предполагать присваиваемость, основанную на отношении "внутренних" типов (A
а также B
).