Suppose I have a generic interface Source<T>
which is a pure producer of T
objects. Being a pure producer is part of the contract of the interface. So it is a reasonable expectation that whatever you can do with a Source<Foo>
, should be also possible to do if you have a Source<? extends Foo>
.
Now I need to enforce this restriction in the body of Source
, so that someone does not accidentally use T
in a way that contradicts that contract.
An example from the JDK
As @Miserable.Variable points out, ArrayList<Integer>
and ArrayList<? extends Integer>
are not equivalent. That's because ArrayList
is not covariant as a generic type. Or in other words, ArrayList<T>
is not a pure producer of T
; specifically, the ArrayList
method add(T)
consumes a T
.
But there are generic types that are pure producers, like Iterator
or Iterable
. Whatever you can do with an Iterator<Integer>
you can also do with an Iterator<? extends Integer>
. There is no method like ArrayList.add(T)
in Iterator<T>
.
I just want to make sure that my interface Source<T>
is like Iterator<T>
rather than like ArrayList<T>
. If someone in the future adds a T
-consuming method (like add(T)
) to my interface, I want them to get an explicit error.
A more complex example
Simply banning parameters of type T
from appearing in the interface is not a full solution. One should also note that T
might be used as argument to other generic types. For example, the following method should not be allowed in Source<T>
:
public void copyTo(List<T> destination);
because an sneaky subclass may try to read from the list, it is considered a T
-consumer; you cannot call this method on a Source<? extends Foo>
. On the other hand, this one should be allowed:
public void copyTo(List<? super T> destination);
(There is also another rule that says methods in Source<T>
cannot return a List<T>
, but can return a List<? extends T>
.)
Now, the actual interface can be arbitrarily complex with lots of methods, and the rules are pretty complex themselves. It is very easy to make a mistake. So I want to automate this check.
Is there a unit-testing trick, static analyzer, compiler/IDE plugin, annotation processor (for example with an @Covariant
annotation on T
), or any other technique or tool that can ensure this for me?
This is not an answer, but too long to fit in a comment.
So it is a reasonable expectation that whatever you can do with a
Source<Foo>
, should be also possible to do if you have aSource<? extends Foo>
No, it is not a reasonable expectation. You linked to an entire pdf and it goes to a top level page so it is unclear how you determined this is reasonable, but in general you cannot arbitrarily replace a Foo<T>
with a Foo<? extends T>
. Foe example, if you have an ArrayList<Integer> a
you can call a.Add(Interger.valueOf(5))
but you cannot do that if a
is ArrayList<? extends Integer> a
.
It is also unclear what are Consumer<T>
and sendTo
. Is the latter a method in Source<T>
>?
Without these clarifications, I am afraid he question is ambiguous.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With