Re: Mixing self conscious parametrized types with inheritance
 
On 03/13/2011 04:13 PM, Lew wrote:
Robert Klemme wrote:
inspired by our recent discussions of generics I set out to get a clear
picture of how to properly use Comparable<T> - especially when
inheritance
comes into play.
1. Only classes whose methods that use the type parameter may not be
overridden with type specific functionality (either because all
methods or the
class is final, or the type need not change when sub classes override
those)
can do away with the type parameter.
Would you please describe this a bit more fully, perhaps with an
example? I'm not quite getting it.
What's missing from the code at Github?  Do you think I should provide 
another example?  FinalC does not need a type parameter because it 
cannot get more specific than this class which is final.  Of course we 
could think of other examples as well, e.g. a class hierarchy where 
parts decide to fix the type parameter:
class Base<C extends CharSequence> {
   public void process(List<C> lc) ...
}
class Derived1<C extends CharSequence> extends Base<C> {
   @Override
   public void process(List<C> lc) ...
}
class Derived2 extends Base<String> {
   @Override
   public final void process(List<String> ls) ...
}
Derived2 does not need a type parameter because someone decided to 
settle with String for the type and disallow overriding of this method 
in sub classes.  Derived2 on the other hand does not use final nor does 
it specify the type.
2. All others need to repeat the type parameter (in case of self
reference
with a bound).
class BaseC<SC extends BaseC<?>> implements Comparable<SC> {
The convention is to use single-letter type parameters.
Well, yes.  From time to time I take the liberty to allow for more 
uppercase letters if I have the feeling this helps remember the purpose 
of the type. :-)
I totally don't get what you're asserting here.
Why do you want 'Base' to implement 'Comparable' on a subtype instead of
on itself?
Because Base is intended for inheritance.  If you settle with 
Comparable<Base> compareTo() has an argument of type Base.  In that case 
your sub classes which might want to implement compareTo() differently 
do not have access to their own members (other than through explicit 
casting which generics are trying to avoid in the first place).  And you 
cannot make sub classes inherit Comparable<SubC> and implement 
compareTo(Subc o) because then type erasure creates a conflict because 
both methods have the same erased signature.  You can see this when 
trying to compile FailedApproach from the Gist 
https://gist.github.com/868085 .
Why the wildcard rather than 'class BaseC <S extends Base <S>>'?
That does not work because it is a recursive type definition - at least 
that's what I believe to be the reason.  Anyway, I my experiments this 
breaks when trying to instantiate the class.  At least I could not find 
a way to do it.  I'd be glad to see how it can be done if it can.
The usual approach is 'class Foo implements Comparable <Foo>', not of
some subtype of 'Foo'.
You can only use this approach if you either do not intend to inherit 
that class OR want sub classes be sorted according to only base class 
state.  In that case it's probably also a good idea to make compareTo() 
final.
I'd like to say:
class Base <S extends Base <S>> implements Comparable <Base <S>>
Mind you, I don't think generics work with multiply-nested type
parameters beyond a certain point, or maybe I just can't mentally
encompass the type assertions they make.
It usually takes me a bit of experimenting and reasoning, too.  Generics 
can be tricky at times.
Just like I haven't mentally encompassed the type assertions you're
trying to make. The difficulties don't seem to involve 'Comparable' so
much as what you're actually attempting to aver.
The example classes compose an inheritance hierarchy of classes which 
all should be comparable but according to class specific criteria (i.e. 
they must have compareTo(MyOwnClass o) instead of just compareTo(Base o)).
@Override
public int compareTo(SC o) {
return getKey() - o.getKey();
}
}
class SubC<SC extends SubC<?>> extends BaseC<SC> {
@Override
public int compareTo(SC o) {
final int cmp = getK() % 3 - o.getK() % 3;
return cmp == 0 ? getK() - o.getK() : cmp;
}
}
final class FinalC extends SubC<FinalC> {
@Override
public int compareTo(FinalC o) {
final int cmp = getX() % 2 - o.getX() % 2;
return cmp == 0 ? getPos() - o.getPos() : cmp;
}
}
Full code is here https://gist.github.com/868085
I have also added a second version which does not compile.  This version 
makes all class implement Comparable<OwnType> but that fails with
The interface Comparable cannot be implemented more than once with 
different arguments: Comparable<BaseC> and Comparable<SubC>
If you want to give all classes in the hierarchy the freedom to have 
their own default ordering (likely based on class specific state) there 
is no other way than to carry a type parameter through the hierarchy.
Kind regards
    robert