Problem of defender methods with generic methods

Steven Simpson ss at comp.lancs.ac.uk
Mon Nov 15 05:46:27 PST 2010


On 15/11/10 10:02, Maurizio Cimadamore wrote:
> On 15/11/10 08:55, Steven Simpson wrote:
>> On 06/11/10 04:55, Ali Ebrahimi wrote:
>>   
>>> public interface CustomizedList<T>  extends List<T>  {
>>>      <A extends Comparable<? super A>>  extension A max() default
>>> Trait.<A>max;<=============  cannot find symbol A
>>>      
>> What is this supposed to mean, especially to a caller?  AIUI,
>> CustomizedList.max will appear to the caller as a regular virtual
>> method, and the caller has no knowlegde that it is somehow bound to
>> Trait.max.
>>    
> I think that the above signature wrong - the problems you see (i.e.
> the fact that you can call max on a List<Runnable>) stems from the
> fact that type-variable A and type-variable T are unrelated.

Quite.  That's sort-of the point I was making - it's an attempt to
relate A and T.  It failed when I tried it, and I wondered whether that
form, or anything else, was mandated.

[snip understood explanation]

> On the other hand, we are talking about adding methods to perform
> _comparisons_ on the contents of a collection - which seem to imply
> that the collection itself must contain elements which are some
> subtype of Comparable. Therefore, I think the right signature would be:
>
> public interface CustomizedList<T extends Comparable<T>>  extends
> List<T>  {
>     extension T max() default Trait.<T>max()
> }

Indeed, though this loses the advantage that a plain old
Collections.max(list) has - the additional constraint isn't applied
anywhere but the call site.  You only need to apply the constraint if
you want to call max, not every time you have a CustomizedList.

Without this deferring of constraint checking (from class to method), we
can't add max to List, so we're forced to use a subtype of List.  Yet it
seems unlikely that someone would devise an API (say) with:

<T>
void doSomething(CustomizedList<T> list) { ... }

...instead of:

<T extends Comparable<? super T>>
void doSomething(List<T> list) { ... }

...just so they could write list.max() instead of Collections.max(list),
while forcing the caller to use a class other than List, which class
doesn't actually bring much advantage to the caller.  To force the
caller in this way suggests that one should always rewrite List<String>
as CustomizedList<String>, knowing that String is Comparable, just in
case the list might eventually be submitted to an API that wants to call
list.max() - which then implies further rewriting when another kind of
List subtype comes along imposing further/alternative constraints on <T>.

It's more plausible, then, that the API writer would use one of the
following options:

   1. Just use List<? extends Comparable> in the interface, with
      Collections.max(..) in the implementation.
   2. As above, but with static extension methods (but still no dynamic
      dispatch on the max call itself).
   3. Add a List defender that imposes additional constraints on <T>, as
      Ali Ebrahimi seems to have tried to do.

So, I guess I'm saying that not supporting option 3 could weaken the
case for defenders, while acknowledging that, if option 3 were to be
supported, there would have to be a way for methods of a generic class
to impose additional constraints on their class's type parameters
without having to apply those constraints to the entire class, and this
information would have to be available at call sites to statically
verify those constraints.

Cheers,

Steven


More information about the lambda-dev mailing list