OldCurmudgeon
OldCurmudgeon

Reputation: 65813

Inheriting Comparable with generics

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?

Upvotes: 4

Views: 155

Answers (4)

Rinke
Rinke

Reputation: 6332

  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>> {
    ...
}

Upvotes: 2

OldCurmudgeon
OldCurmudgeon

Reputation: 65813

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.

Upvotes: 1

Binkan Salaryman
Binkan Salaryman

Reputation: 3048

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

Upvotes: 0

Andremoniy
Andremoniy

Reputation: 34900

Just change ThingHolder class signature to:

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

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

Upvotes: 0

Related Questions