Re: Mixing self conscious parametrized types with inheritance
On 13 Mrz., 23:38, Lew <no...@lewscanon.com> 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 inherita=
nce
comes into play.
People will have to review the thread if they want the part I elided.
Definitively.
I think the problem is that the base class is 'implements Comparable <S>'=
..
That really isn't the type assertion for 'Comparable'. The
self-referentiality is tangling the type assertions, for sure, but the re=
al
problem is that you want the subclass to implement its own 'compareTo()'
separate from the superclass's. The wildcard dodge weakens the type
assertions to the point where you get away with it.
That's an unfriendly way to state it. :-) I rather like to think of
having found the proper generics solution for a given problem.
Normally a comparable class 'Foo' uses 'implements Comparable<Foo>'. Y=
ou
didn't do that. If you had, you'd wind up with:
class BaseC<SC extends BaseC<?>> implements Comparable<BaseC<SC>> {
@Override
public int compareTo( BaseC<SC> o) {
return getKey() - o.getKey();
}
}
You cheated by having 'BaseC#compareTo()' take a different type argument =
from
what it's supposed to take. Yes, you did subvert the generics system b=
y doing
that, but only by changing the semantics of what you're asserting.
I can't detect the cheating. I simply deferred specification of
Comparable's type parameter to the point in time when the type is
known. Since we cannot specify Comparable's type parameter in the
base class (because sub classes need different types here) we must
keep it undefined (i.e. need a type variable for it). That's
basically the general generics approach. It may be obfuscated a bit
through the fact that the type we want to use is always the class we
are actually writing but it's nevertheless the generics mantra of "if
you don't know the type yet, use a type parameter".
Then you'd have
Not "would" but "do": https://gist.github.com/868085#file_self_consciousnes=
s_test.java
class SubC<SC extends SubC<?>> extends BaseC<SC> {
@Override
public int compareTo( SubC<SC> o) {
final int cmp = getK() % 3 - o.getK() % 3;
return cmp == 0 ? getK() - o.getK() : cmp;
}
}
which yields the correct compiler error that you had subverted, with or
without a clause 'implements Comparable<SubC<SC>>'.
I also wonder about the 'extends BaseC<SC>' part. Generics aren't cova=
riant
through the type parameter without some 'extends' magic, so I think this =
part
tangles the type assertions also. I haven't thought it through yet.
Anyway, you have found a corner where the generics assertions cannot hand=
le
what you wanted to do. That part is true.
No, I haven't found a corner case generics cannot handle. The code at
https://gist.github.com/868085#file_self_consciousness_test.java
exactly shows how to handle the situation. The other example was to
demonstrate that the approach with class BaseC implements
Comparable<BaseC> does NOT work: https://gist.github.com/868085#file_failed=
_approach.java
It is also true that formally you
want to make different type assertions from those to properly implement
'Comparable'. When you make the correct type assertions, the compiler =
chokes
on the erasure demon.
You might be able to get away with the kind of self-referentiality you wa=
nt
better with interfaces at the root than with classes. That's a good fo=
llowup
line of inquiry.
Actually the working example is exactly what should be done here. We
want a class hierarchy where every class has its own default
ordering. It follows we want Comparable and it also follows that we
cannot use Comparable with a specific type but we need to propagate
the type parameter from the most specific type upwards. That requires
a type parameter at the base class and all sub classes which can have
sub classes that need a different type propagated.
Kind regards
robert