views:

156

answers:

4

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?

+6  A: 

It's a little but 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;
    }

}
Willi
I think that's it.
Pointy
Consider (1) class MyImpl1 extends MyClass<MyImpl1> { ... }; and (2) class MyImpl2 extends MyClass<MyImpl1> { public int compareTo (MyImpl1 o ) {...} }. MyImpl2 is not doing the right thing.
emory
If we assume that every subclass extends MyClass with its own class as the generic parameter, the solution is correct. However, it seems that there is no way ensure this, as emory pointed out.
Cem
@emory This is also true for implementing `Comparable` directly. No one stops you from doing `MyImpl2 implements Comparable<MyImpl1>`.
Willi
@Cem, @Willi If we assume that every subclass extends MyClass with its own class as the generic parameter this will work. But we might as well assume that every subclass implements Comparable with its own class as the generic parameter.
emory
But declaring the requirement *forces* sub classes to implement comparable instead of *assuming* it.
Willi
+1  A: 

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);
zevra0
Isn't this exactly the way Cem described his problem? How would you implement `compareTo` in `MyClass1` or `MyClass2` with a different parameter type?
Willi
You're correct... read the question the other way.
zevra0
+6  A: 

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.

seh
I agree with this answer. In addition, we should be coding to interfaces not classes. The actual implementation class may be an anonymous class, local class (nested inside a method), private (nested inside a class), or package private and thus not within our scope.
emory
Another problem I see is storing the subclass objects in a Collection. I should be able to store them with just `List<MyClass>`, instead of `List<MyClass<?>>`. And what would get called when you get one of those objects and call `equals(anObject)`?
TheLQ
seh, thanks for your answer. Actually, I am looking for a contract that will force every subclass to have a compareTo() method only for its own class, but not for any other subclass. My made-up generics definition might be misleading in this sense.
Cem
seh
+2  A: 

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 MyClas<X> defines a contract for compareTo(X) which all of its subtypes must honor. there is no problem there.

irreputable
That's a good example, though it differs from Cem's original question. Now, I may have been reading his code too literally; perhaps this is exactly what he was *trying* to write. In this case, the `Comparable` facet of the `Enum` interface is about what a specific subclass can do with *itself* (or, rather, instances of itself), not what it can do with `Enum`-derived types in general. If that's what Cem was after, then I take your answer to be more appropriate than mine.
seh
I guess it is OK as long as all subclasses are a subtype of MyClass<?>. I am not sure, though, if it causes a problem in future steps. This is the first time I am designing with generics.
Cem