@Observes methods may now declare an interface parameter, not just a concrete
class. When an event is fired, Pixie selects the most-specific matching observer
across the full type graph — classes and interfaces — using the same "most
specific wins" rule the JVM applies to overloaded method calls.
public interface Auditable {}
public class Listener {
public void onAuditable(@Observes Auditable event) {
// receives any event whose type implements Auditable
}
}When an event matches two unrelated observed interfaces on the same component and
no common subtype breaks the tie, there is no most-specific match. Rather than
guess, Pixie throws AmbiguousObserverException when the event is fired — the
same stance the Java compiler takes on an ambiguous overloaded call. Resolve it by
observing a shared subtype or removing one of the observers.
Declaring two @Observes methods for the same type on a single component is now
rejected when the component is registered, instead of silently dropping one.
A single configuration property can now be injected as a List, Set, or Map
when the parameter declares a generic element type. Each element passes through
the normal @Param conversion chain, so any type usable as a scalar @Param
also works as a collection element.
public class FeedReader {
public FeedReader(@Param("uris") final List<URI> uris) {
// ...
}
}reader = new://org.example.FeedReader
reader.uris = http://one, http://two/dosLists and sets split on commas (surrounding whitespace is tolerated). Maps use
the standard Java properties format — one key=value pair per line — and both
keys and values are converted.
Supported declared types:
-
List,Collection,ArrayList→ArrayList -
Set,HashSet→HashSet -
SortedSet,TreeSet→TreeSet -
Map,HashMap→HashMap -
SortedMap,TreeMap→TreeMap
Component references (@Component) now use generic type arguments when resolving which
components are eligible for injection. Previously, only the raw type was considered — a
parameter of type RequestHandler<String, Integer> would match any RequestHandler
implementation. Now, only implementations with matching type arguments are injected.
This applies to:
-
Single
@Componentrefs resolved by type or by name -
Collection injection (
List<RequestHandler<String, Integer>>) — only matching implementations are collected -
All producer types — constructor-based,
@Factorymethods,@Builderpatterns, and pre-built instances viasystem.add()
Injection points can use Java wildcards:
-
? extends Number— matches any subtype of Number -
? super Integer— matches any supertype of Integer -
?— matches any type argument (unbounded) -
Nested parameterized bounds such as
? extends Comparable<String>
Type arguments can come from multiple sources and are correctly stitched together:
-
Some from the factory/builder method declaration
-
Others from the class hierarchy of the produced type
For example, a factory returning BooleanHandler<String> where BooleanHandler<I> implements RequestHandler<I, Boolean>
correctly resolves to RequestHandler<String, Boolean>.
Raw type injection points (no generics) continue to match any implementation, preserving full backwards compatibility with existing code.