Reputation: 743
I am trying to define an abstract class implementing Comparable. When I define the class with following definition:
public abstract class MyClass implements Comparable <MyClass>
subclasses have to implement compareTo(MyClass object)
. Instead, I want every subclass to implement compareTo(SubClass object)
, accepting an object of its own type. When I try to define the abstract class with something like:
public abstract class MyClass implements Comparable <? extends MyClass>
It complains that "A supertype may not specify any wildcard."
Is there a solution?
Upvotes: 74
Views: 36118
Reputation: 587
Found another solution:
Solution should look like this:
public abstract class MyClass implements ComparableFoo,Comparable<ComparableFoo> {
public int compareTo(ComparableFoo o) {
// your implementation
}
}
This solution implies that more things might implement ComparableFoo - this is likely not the case but then you're coding to an interface and the generics expression is simple.
Upvotes: 1
Reputation: 121
I know you said you want "compareTo(SubClass object), accepting an object of its own type", but I still suggest declaring the abstract class like this:
public abstract class MyClass implements Comparable <Object>
and do an instanceof check when overriding compareTo in MySubClass:
@Override
public int compareTo(Object o) {
if (o instanceof MySubClass)) {
...
}
else throw new IllegalArgumentException(...)
}
similarly to 'equals' or 'clone'
Upvotes: 0
Reputation: 15259
Apart from the mechanical difficulties you're encountering declaring the signatures, the goal doesn't make much sense. You're trying to establish a covariant comparison function, which breaks the whole idea of establishing an interface that derived classes can tailor.
If you define some subclass SubClass
such that its instances can only be compared to other SubClass
instances, then how does SubClass
satisfy the contract defined by MyClass
? Recall that MyClass
is saying that it and any types derived from it can be compared against other MyClass
instances. You're trying to make that not true for SubClass
, which means that SubClass
does not satisfy MyClass
's contract: You cannot substitute SubClass
for MyClass
, because SubClass
's requirements are stricter.
This problem centers on covariance and contravariance, and how they allow function signatures to change through type derivation. You can relax a requirement on an argument's type—accepting a wider type than the supertype's signature demands—and you can strengthen a requirement on a return type—promising to return a narrower type than the supertype's signature. Each of these freedoms still allows perfect substitution of the derived type for the supertype; a caller can't tell the difference when using the derived type through the supertype's interface, but a caller using the derived type concretely can take advantage of these freedoms.
Willi's answer teaches something about generic declarations, but I urge you to reconsider your goal before accepting the technique at the expense of semantics.
Upvotes: 21
Reputation: 122449
public abstract class MyClass<T> implements Comparable<T> {
}
public class SubClass extends MyClass<SubClass> {
@Override
public int compareTo(SubClass o) {
// TODO Auto-generated method stub
return 0;
}
}
Upvotes: 1
Reputation: 45443
see Java's own example:
public abstract class Enum<E extends Enum<E>> implements Comparable<E>
public final int compareTo(E o)
on seh's comment: usually the argument is correct. but generics makes type relations more complicated. a SubClass may not be a subtype of MyClass in Willi's solution....
SubClassA
is a subtype of MyClass<SubClassA>
, but not a subtype of MyClass<SubClassB>
type MyClass<X>
defines a contract for compareTo(X)
which all of its subtypes must honor. there is no problem there.
Upvotes: 3
Reputation: 5120
It's a little too verbose in my opinion, but works:
public abstract class MyClass<T extends MyClass<T>> implements Comparable<T> {
}
public class SubClass extends MyClass<SubClass> {
@Override
public int compareTo(SubClass o) {
// TODO Auto-generated method stub
return 0;
}
}
Upvotes: 53
Reputation: 271
I'm not sure that you need the capture:
First, add the compareTo to the abstract class...
public abstract class MyClass implements Comparable <MyClass> {
@Override
public int compareTo(MyClass c) {
...
}
}
Then add the implementations...
public class MyClass1 extends MyClass {
...
}
public class MyClass2 extends MyClass {
...
}
Calling compare will call the super type method...
MyClass1 c1 = new MyClass1();
MyClass2 c2 = new MyClass2();
c1.compareTo(c2);
Upvotes: 1