3

I am struggling to get an interface implementation to be inherited in another class:

I have a generic class that requires compareTo to be implemented. The class is called GenericList and is implemented as:

public class GenericList<T extends Comparable<T>> {
    private T[] items;
    private int count;
    public GenericList() {
        items = (T[]) new Comparable[10];
        count = 0;
    }
}

I have a User class that implements compareTo:

public class User implements Comparable<User> {
    private String email;

    public User(String email) {
        this.email = email;
    }

    @Override
    public int compareTo(User o) {
        return (email.compareTo(o.email));

    }
}

I have no problem creating a GenericList of Users:

var users = new GenericList<User>();

If I create a class that extends User and try to create a generic list of that type, I get an error. I have created a class called Instructor:

public class Instructor extends User{
    public Instructor(String email){
        super(email);
    }
}

If I create a generic list using that class:

var instructors = new GenericList<Instructor>();

I get an error:

Type parameter 'com.martin.Instructor' is not within its bound; should implement 'java.lang.Comparable<com.martin.Instructor>'

Shouldn't it use the inherited compareTo method?

I've tried lots of different ways but I can't get GenericList to use an inherited class like this.

Thanks

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • 3
    Instructor inherits `compareTo(User)`. The interface requires `compareTo(Instructor)`. – Andy Thomas Jan 24 '22 at 22:25
  • 3
    To clarify, `Instructor` needs to implement `int compareTo(Instructor)` even if it just delegates to its superclass. – Jim Garrison Jan 24 '22 at 22:27
  • 1
    youcan try to declare `public class GenericList> { private T[] items;` – Iłya Bursov Jan 24 '22 at 22:29
  • 1
    Also, that cast in the GenericList constructor skates by now with type erasure, but would break if we ever get reified generics. – Andy Thomas Jan 24 '22 at 22:30
  • 1
    Martin... think about the previous two comments logically. `Instructor` can have additional fields that are not accounted for when you compare just a generic `User`. That's why `Instructor` must have a `compareTo()` method. – hfontanez Jan 24 '22 at 22:30
  • 1
    Thanks to all for your advise. I got it working with `public class GenericList>` – Martin Williams Jan 24 '22 at 22:39

2 Answers2

4

public class GenericList<T extends Comparable<T>>

When you declare GenericList<Instructor>, then the above declaration replaces T with Instructor. So now it says that Instructor must extend (or implement, really) Comparable<Instructor>. The problem is that Instructor extends User which implements Comparable<User> but doesn't implement Comparable<Instructor>.

So the problem is well before trying to find the inherited compareTo() method. One way to fix the immediate compiler error is to change the GenericList declaration:

public class GenericList<T extends Comparable<? super T>>

This uses a type capture on the Comparable interface.

Now fair warning, I have check that this change will compile here, but otherwise I have not tested it because your question doesn't provide any usages of GenericList once you create it.

rgettman
  • 176,041
  • 30
  • 275
  • 357
Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
1

The overall logic seems to be correct: User and Comperable are the supertypes of the Instructor class, i.e. instance of Instructor can represent them both. But here's a catch:

var ints1 = (Comparable<Instructor>) new Instructor("name"); // this line will not compline
var ints2 = (Comparable<User>) new Instructor("name"); // no errors here

The first line will cause a compilation error because there's no implementation of the Comparable<Instructor> in the inheritance chain of the Instructor class, but there's an implementation of the Comparable<User> which is the class User itself. And hence the signature of compareTo() method that Instructor class inherits will be compareTo(User user).

A quick reminder: generics are invariant, i.e. to a variable of type List<Object> we can assign only List<Object>, not List<Person> or List<Cat>. Similarly types Comparable<User> and Comparable<Instructor> are not comparable with each other regardless of the fact that User is a super-type of Instructor. That's what is called invariance and the main purpose of it is to enforce type-safety.

As the conscience of this, it isn't possible to coerce Instructor object to Comparable<Instructor> type.

A possible remedy is to implement Comparable in the Instructor class, it will be useful only if Instructor defines some own properties that establish uniqueness of its instances, or change the declaration of generic type as already shown here

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46