4

Trying to design a superclass that ensures all sub-classes are inherently Comparable.

/**
 * A base class implementing Comparable with itself by delegation.
 * @param <T> - The type being wrapped.
 */
static class Distinct<T extends Comparable<T>> implements Comparable<Distinct<T>> {
    final T it;
    public Distinct(T it) {
        this.it = it;
    }
    @Override
    public int compareTo(Distinct<T> o) {
        return it.compareTo(o.it);
    }
}
/**
 * A set of distinct items.
 *
 * @param <T>
 */
static class ThingHolder<T extends Comparable<T>> {
    final Set<T> things;
    public ThingHolder() {
        this.things = new TreeSet<>();
    }
}
/**
 * A sample real thing.
 */
static class Thing extends Distinct<String> {
    public Thing(String it) {
        super(it);
    }
}
// This doesn't work - Why?
final ThingHolder<Thing> yz = new ThingHolder<>();

The error I get reads:

com/oldcurmudgeon/test/Test.java:[70,22] error: type argument Thing is not within bounds of type-variable T
  where T is a type-variable:
    T extends Comparable<T> declared in class ThingHolder

Why is this not working? Can it be done?

Kannan Thangadurai
  • 1,117
  • 2
  • 17
  • 36
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • I think `static class Distinct> implements Comparable> ` should be `static class Distinct> implements Comparable` – Héctor Nov 12 '15 at 09:43
  • @bigdestroyer - BUt that - in my case - would be `implements Comparable`, I want to compare them with each other, not with the generic type. – OldCurmudgeon Nov 12 '15 at 09:45
  • Nice puzzle about java type variables :) – Andremoniy Nov 12 '15 at 09:52
  • `static class ThingHolder { final Set> things;...` compiles but I've yet to figure out whether it does what it should. Update: No, it doesn't. – biziclop Nov 12 '15 at 09:57
  • @biziclop - That's an option but it would leave the `Comparable` contract to run-time - which may not be the best option. – OldCurmudgeon Nov 12 '15 at 10:00
  • @OldCurmudgeon Yes, I'm just thinking aloud. – biziclop Nov 12 '15 at 10:01
  • Your `Distinct` class declaration basically says that any subclass of `Distinct` is comparable to any other subclass of `Distinct`. That doesn't sound correct to me, and it's at the heart of the problem. `Thing implements Comparable>`, not `Comparable`. – biziclop Nov 12 '15 at 10:09
  • @biziclop - Surely that would be the case if I used `implements Comparable>` - I am using `implements Comparable>` suggesting it is comparable with objects of the same class only - or am I wrong? – OldCurmudgeon Nov 12 '15 at 10:17
  • 1
    I'm struggling to explain it, you can try to replace `T` with `String` across the board to see what I mean. – biziclop Nov 12 '15 at 10:28

4 Answers4

2
  1. If you pass a type argument X to ThingHolder it has to be a subtype of Comparable<X> (by the class declaration of ThingHolder).
  2. So, if you pass the type Thing to ThingHolder it has to be a subtype of Comparable<Thing>. (Follows from the previous statement by substitution of Thing for X.)
  3. Thing extends Distinct<String> and therefore implements Comparable<Distinct<String>> (by the class declaration of Thing).
  4. Thing is not the same type as Distinct<String> - although it's a subtype - and therefore type matching fails.

You could fix this by adjusting the class declaration of ThingHolder as follows:

class ThingHolder<T extends Comparable<? super T>> {
    ...
}
Rinke
  • 6,095
  • 4
  • 38
  • 55
  • Self-bounded type parameters do work in general though. – biziclop Nov 12 '15 at 10:03
  • I accept that weakening the contract a little avoids the problem - like @Andremoniy suggested using `extends Comparable` but that still does not explain why the full strength contract does not work. – OldCurmudgeon Nov 12 '15 at 10:05
  • My explanation made no sense, sorry. I fixed the answer. – Rinke Nov 12 '15 at 10:19
  • But `Distinct` implements `Comparable>` and therefore should match the `>` of `ThingHolder` - yet it does not. – OldCurmudgeon Nov 12 '15 at 10:22
  • I see you adopted my answer... The need to use ```` constraints are always the cause of bad design; there must be a better solution for what you are trying. – Binkan Salaryman Nov 12 '15 at 10:23
  • @OldCurmudgeon No, unfortunately that's not true. The Liskov substitution principle holds for objects (based on their type), but _doesn't_ hold for types themselves. This is a super confusing thing about type parameters, I know. Use `extends` and `super` to get similar behavior. See my updated answer. – Rinke Nov 12 '15 at 13:57
  • I got there by a different route but essentially this is the best solution - although it may not be perfect as I note in my answer. – OldCurmudgeon Nov 12 '15 at 14:23
1

Some research and head-bashing brought up this suggestion.

In general, if you have an API that only uses a type parameter T as an argument, its uses should take advantage of lower bounded wildcards (? super T).

Some tinkering (adding ? super T) to slightly relax the restrictions leaves:

/**
 * A set of distinct items.
 *
 * Don't like the <?> in there but a <T> is not accepted.
 *
 * @param <T>
 */
static class ThingHolder<T extends Comparable<? super T>> {

    final Set<T> things = new TreeSet<>();

}

final ThingHolder<Thing> holder = new ThingHolder<>();

and this is acceptable to the compiler.

Generally I don't like using ? to paper over the gaps because it usually allows too much through but in this case I will leave it at that.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
0

Just change ThingHolder class signature to:

static class ThingHolder<T extends Comparable> {
       ...    
}

I.e. remove <T> from Comparable, it isn't necessary.

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • That seems to get around the problem - but why? This feels like using raw types as comparable is defined as `Interface Comparable`. – OldCurmudgeon Nov 12 '15 at 09:54
  • I think there is some littel confusion here. Type variables are only some kind of restriction for compile time. They aren't anything existent. So, in your case with class `ThingHolder` you only declare that `T` should be `Comparable`, and it doesn't make sence for it - be comparable with it self, because it is implicitly so. – Andremoniy Nov 12 '15 at 09:56
  • @Andremoniy You can always declare `class Foo implements Comparable`, which is of course stupid, but valid. On the other hand you can say it isn't `Distinct`'s responsibility to handle such pointless class declarations. – biziclop Nov 12 '15 at 10:01
  • Yes, but in this case `Distinct`, not `Distinct`. – Andremoniy Nov 12 '15 at 10:03
0

class ThingHolder<T extends Comparable<T>> declares that the thing T must be comparable to itself, but class Thing extends Distinct<String> isn't.

Binkan Salaryman
  • 3,008
  • 1
  • 17
  • 29