It is better to think of
? as “some unknown subtype.” So e.g. a
Collection<? extends Number> means “a
Collection typed on some unknown subtype of
Number.” It might be a
Collection<Integer> but it might be a
Collection<Double>. At this scope, the compiler does not know which, and so only allow operations that would work with any possibility.
The way it is phrased above implies that a
Collection<SomeClass> would only accept objects typed on exactly
SomeClass rather than
SomeSubclass extends SomeClass, which is not the case. Whereas a
Collection<? extends SomeClass> will not accept a
SomeClass object as argument to e.g.
add, since the collection is actually typed on some unknown subtype of
The rule of thumb for API is:
- If possible, use a type variable in the method declaration when the method takes an input involving that variable; e.g.
<B extends Bar> void processFoo(Foo<B> foo);
- If you don’t want/need to use a type variable, then favor
Foo<? extends Bar> over
Foo<Bar> as method argument, so that callers can pass e.g.
Foo<? extends Bar>, as return type. Because getting a
Foo<? extends Something> back from a method call causes lots of difficulties.
- Lastly, do not use a type variable when the method does not have an argument involving that variable. E.g.,
Foo<B> computeFoo(String expression) causes problems, because there is no way for the code at runtime to discern which
B you need. The compiler allows this in certain circumstances, but it is not type safe. There are currently some places in Ops that need fixing to avoid this issue (see e.g. imagej/imagej-ops#160).
In other words: be permissive in what you accept, and strict in what you produce. Of course, like any rule of thumb, there are exceptions.
If you see places this rule of thumb is violated in Ops, feel free to point it out, and we can discuss whether the design could be improved there.
The situation gets nasty with nested generics, and incredibly nasty when the generics are recursive. A
? extends RealType<?> is effectively useless, because the compiler does not understand that those two wildcards actually must be the same. Think about the more common situation of
? extends Collection<?>—obviously those
? are two different placeholders. But for
? extends RealType<?> they actually must match due to the recursive declaration of
T extends RealType<T>.
In short: never use
? extends RealType<?> or
RealType<?>. Always use a type variable (e.g.,
T), even if you have to create an extra method signature to declare it.