javalistgenericsarraylistbounded-wildcard

JAVA 8 - Implementing Comparable in a user-defined class that uses generics - Collections.sort() not working


I have a user-defined class that is supposed to be generic to accept Integers, Floats, Doubles, etc. but also needs to be able to be compared. It looks like:

public class MyClass<T extends Comparable<T>> implements Comparable<MyClass<T>>{
private T myClassObject;
...

And the compareTo method looks like

@Override
public int compareTo(MyClass<T> o){
    return this.myClassObject.compareTo((T) o));
}

Now in a later class that is supposed to be creating an ArrayList of MyClass objects and comparing them, I get:

sort(java.util.List<T>)' in 'java.util.Collections' cannot be applied to '(java.util.ArrayList<MyClass<?>>)

The ArrayList that holds these MyClass objects looks like:

ArrayList<MyClass<?>> temp = new ArrayList<MyClass<?>>();

Is it failing because of the wildcard generic use? Do I need to specify that ? extends Number or something else?


Solution

  • The T in the List<T> parameter in the generic Collections.sort method, enforces that you pass in a list with elements that can be compared with each other.

    A List<MyClass<?>> can have elements that are not comparable with each other.

    With a List<MyClass<?>>, you can potentially put a MyClass<String> and a MyClass<Integer> into the same list!

    ArrayList<MyClass<?>> temp = new ArrayList<MyClass<?>>();
    temp.add(new MyClass<>(1));
    temp.add(new MyClass<>("String"));
    

    To sort this list, Collections.sort would have to know how to compare a MyClass<Integer> with a MyClass<String>! And that's nonsense. More importantly, Comparable<T> doesn't support this operation.

    List.sort - a non-generic method - on the other hand, doesn't have this "equality constraint", and does a bunch of unsafe casts, and will throw an exception at runtime if you try to sort a list with different kinds of MyClass.


    Though unrelated to the error, note that you implemented Comparable wrongly. It should be:

    return myClassObject.compareTo(o.myClassObject);