Re: Mixing self conscious parametrized types with inheritance
On 14 Mrz., 13:24, Lew <no...@lewscanon.com> wrote:
Robert Klemme wrote:
Lew wrote:
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=
real
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.
"Unfriendly"?
It's weaker to say "some subtype of Object" than "type Foo". It's not =
as
strong an assertion.
It's no unfriendlier than to say that 100 newtons is a weaker force than =
1000.
It's also not a proper solution because of your dodge on the declaration =
of
'Comparable' implementation.
The "dodge" and "get away" bits are the "unfriendly" ones. :-) You
call what I would call a "proper solution" to a given problem "a
workaround". Why do you insist that only Comparable<ConcreteType> is
valid and not Comparable<TypyParameter>? This is general practice,
e.g.
public class Processor<T> {
private final List<T> processed = new ArrayList<T>();
public void process(T it) {
processed.add(it);
}
}
Here List<T> is also unspecified and will only be fixed when I
instantiate Processor<String> or Processor<Integer> etc.
Normally a comparable class 'Foo' uses 'implements Comparable<Foo>'. =
You
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 argume=
nt from
what it's supposed to take. Yes, you did subvert the generics syste=
m by doing
that, but only by changing the semantics of what you're asserting.
I can't detect the cheating. I simply deferred specification of
The cheating is that you implement 'Comparable<SC>' ionstead of
'Comparable<Base<SC>>'. The non-cheating way is to implement 'Comparab=
le' of
the class you want to compare, not the type parameter. Do you see that=
you
did not implement 'Comparable<Base<SC>>' but instead 'Comparable<SC>'? =
If you
see that, then you've detected the cheating.
I see it but I fail to recognize the cheating (see above). Also,
please note that SC is bound (SC extends BaseC<?>). This also means
that
public int compareTo(SC o) {
can work with the fact (i.e. access specific members) that SC is
BaseC<?> or a sub class.
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_conscious=
ness_test.java
Sorry, but that is just untrue. In there you have
class BaseC<SC extends BaseC<?>> implements Comparable<SC> {
I just now copied-and-pasted that line into this post, so I'm telling the=
truth.
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 o=
r
without a clause 'implements Comparable<SubC<SC>>'.
I also wonder about the 'extends BaseC<SC>' part. Generics aren't c=
ovariant
through the type parameter without some 'extends' magic, so I think th=
is part
tangles the type assertions also. I haven't thought it through yet.
Anyway, you have found a corner where the generics assertions cannot h=
andle
what you wanted to do. That part is true.
No, I haven't found a corner case generics cannot handle. The code a=
t
https://gist.github.com/868085#file_self_consciousness_test.java
exactly shows how to handle the situation. The other example was to
It shows how to handle it incorrectly because of the 'Comparable' dodge.
demonstrate that the approach with class BaseC implements
Comparable<BaseC> does NOT work:https://gist.github.com/868085#file_=
failed_approach.java
Right, because it bumps up against the erasure issue.
It is also true that formally you
want to make different type assertions from those to properly implemen=
t
'Comparable'. When you make the correct type assertions, the compil=
er chokes
on the erasure demon.
You might be able to get away with the kind of self-referentiality you=
want
better with interfaces at the root than with classes. That's a good=
followup
line of inquiry.
Actually the working example is exactly what should be done here. We
Except for implementing 'Comparable' on the type parameter instead of the
class, which is not "exactly what should be done", it's "exactly what sho=
uld
not be done" because it makes for convoluted, hard-to-understand and
not-exactly-what-you-want type assertions.
The problem is that you aren't declaring 'class Foo implements
Comparable<Foo>'. You're declaring 'class Foo implements Comparable<No=
tFoo>'.
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
You have found a workaround for the erasure issue. The problem is that=
you
created a generics structure that doesn't declare exactly what you mean a=
nd is
difficult to reason about. OTOH, the difficulty with the "normal" appr=
oach is
that you cannot get Java generics to do what you want that way.
the type parameter from the most specific type upwards. That require=
s
a type parameter at the base class and all sub classes which can have
sub classes that need a different type propagated.
Well, it requires that you cheat, in the sense that you have to warp the
declaration of 'implements Comparable'.
I don't see why I "warp" the declaration.
Notice I'm not saying one shouldn't do what you suggest, only that it req=
uires
some dodgy type assertions to get around erasure and playing loose with t=
he
semantics of 'implements Comparable'.
I cannot find anything in http://download.oracle.com/javase/6/docs/api/java=
/lang/Comparable.html
that would explain the dodgyness of my solution and render it a
workaround instead of a proper solution. What am I missing?
Kind regards
robert