I think your search method is parameterized incorrectly. In particular, you almost certainly don't want to use extensions in the R extends T declaration.
You seem to agree that with the wildcard common parameter <? extends Item> <? extends Item> you cannot insert anything because the compiler cannot claim that any particular object you pass matches borders (with the exception of a null literal). Remember that this is not due to any special semantics of the insert-type method, but solely because of the interface.
Your find method cannot be called for the same reason. Keep in mind that declaring <R extends T> , and then declaring a parameter of type R , exactly matches the declaration of a parameter of type T (Think about it, valid values ββin both cases). And, as you saw above, no nonzero objects can be accepted as an instance of T in your substitution case.
I think you might have intended to write your search method as <R super T> . In this case, the compiler can know for sure that regardless of the actual type of T , it is Item or a subtype - and therefore Item or any of its superclasses (including Object) will always be valid for R and, therefore, can be passed. However, in this case, since Object is a valid replacement for borders, and all objects can be accepted for the Object parameter, this method is then equivalent
public int find(Object r) { return r.hashCode(); }
This is a fact - this is the full semantics that you are trying to capture - you do not need general limitation, because they do not provide any boundaries. Usually it is always worth using super within common bounds when it is a nested common parameter, for example. you take the collection as the parameter you want to add objects of type T to (in this case, you need Collection<? super T> ).
Alternatively, after reading your own answer to the question, my assessment in the above paragraph may be a little wrong. There are three different restrictions that you might try to apply to the argument type of the find method:
- Everything is valid (i.e.
Object ). - The argument must be an instance of the maximum possible boundary for the contained types (therefore, if the Container is defined as
Container<T extends Item> , you declare a method to accept a parameter of type Item ). - Types must match exactly (i.e.
T ).
In general, I would recommend going as general as possible, if you need to call the methods that are defined in the Item class to check compliance, then your hands are tied and you have to go with the second option. However, if you do not need to do this, then accept arguments of type Object to provide subscribers with the greatest flexibility.
Along these lines there is practically no argument for accepting option 3 - you will not get any additional functions in your method (since you cannot call more specific methods than you could in the second case), and you simply limit clients. Consider the following:
MyItem a = new MyItem(); Container<MyItem> c = new Container<MyItem>(); c.insert(a);
There is no benefit in forcing callers to reset the Item link to T , in particular when, by definition, you can make the required method calls inside the find object of the Item object and you can return the corresponding answer based on the actual state of the object, and not on the link that he is currently holding.