Search code examples
javagenericscomparatorcomparable

Comparable and Comparator Interface in Java


I want to write a generic Pair class, which has two members: key and value. The only requirement to this class is that both key and value should implements the Comparable interface, otherwise Pair class will not accept them as type parameter.
First I code it like this:

public class Pair<T1 extends Comparable, T2 extends Comparable>

But the JDK 1.6 compiler will generate warning about this:

Comparable is a raw type. References to generic type Comparable<T> should be parameterized

Then I tried to add type parameters and the code now looks like this:

public class Pair<T1 extends Comparable<? extends Object>,
                  T2 extends Comparable<? extends Object>>

Now everything go well until I tried to generate an Comparator for Pair.(The following code is in Pair class)

public final Comparator<Pair<T1, T2>> KEY_COMPARATOR = new Comparator<Pair<T1, T2>>() {
        public int compare(Pair<T1, T2> first, Pair<T1, T2> second) {
            *first.getKey().compareTo(second.getKey());*
            return 0;
        }
    };

The code first.getKey().compareTo(second.getKey()); will generate an error saying:

The method compareTo(capture#1-of ? extends Object) in the type Comparable<capture#1-of ? extends Object> is not applicable for the  arguments (T1)

Anyone knows what does this error message mean?
Any hints on this topic are welcome.

UPDATE:
Here is the complete code:

public class Pair<T1 extends Comparable<? extends Object>, T2 extends Comparable<? extends Object>> {
    private T1 key;
    private T2 value;

    public static int ascending = 1;
    public final Comparator<Pair<T1, T2>> KEY_COMPARATOR = new Comparator<Pair<T1, T2>>() {
        public int compare(Pair<T1, T2> first, Pair<T1, T2> second) {
            int cmp = first.getKey().compareTo((T1)(second.getKey()));
            if (cmp > 0)  return ascending;
            return -ascending;
        }
    };
}

@MarvinLabs Can you explain a bit more why the compiler cannot make sure objects are compared to other objects of the same type. In the above code, second.getKey() returns T1 type, which is of the same type as first.getKey()


Solution

  • Lets get a look at interface design first.

    public interface Comparable<T> { 
    
       public int compareTo(T o);
    
    } 
    

    It is quite typical we must say. So if our class needs to implement it we do this.

    pubilc class ICanComparteWithMyself implements Comparable<ICanComparteWithMyself> { 
    
    public int compareTo(ICanComparteWithMyselfo)    
       //code for compration
    } 
    

    As we see the generic parameter type, determine on what we will operate, so for generics we act in the same way

    public class ICanCompareMyGeneric<T> implements Comparable<T> {
    
       public int compareTo(T o)    
           //code for compration
        } 
    }
    

    In your case whawt we want it to assure that generic parameters implements is own Comparable, for that we need to do this

    public class MyGenericCanCompareToItself<T extends Comparable<T>> { 
    
    }
    

    As we can see, this is quite common to use. The limitation expected (or not) is that we can work on classes that implement Comparable for it self type. If we had

     public class ICanCompareStrings implements Comparable<String> {
          public int compareTo(String o)    
               //code for compration
          }
     }
    

    So for class MyGenericCanCompareToItself as generic parameter we can use class public MyGenericCanCompareToItself but not ICanCompareStrings.

    EDIT:

    So when we covered the basics now we can go to solve, your problem

    Your class description looks like this

    public class Pair<T1 extends Comparable<? extends Object>, T2 extends Comparable<? extends Object>>

    This do not have much sensce as is more less the same as <?>

    This description says:

    I am a Pair class, that work with two generic parameters, that can use comparison over something that I do not know.

    With this code you cann not progress before the generic parameters do not know on that then operate there for you end up with something like this.

    first.getKey.compareTo(null);

    And this why you code do not compile when you try to cast, the expected type is null.


    To change that you need to qualify on what type your generic parameters should be comparable.

    For example the can compare on itselft

    public class Pair<T1 extends Comparable<T1>, T2 extends Comparable<T2>>

    This descriptions says:

    I am a Pair class that works with two parameters, that each of them can be compared with itself.

    And this is what you probably are looking for, additionally they can be compared on something that can be super class of T1 or T2

    public class Pair<T1 extends Comparable<? super T1>, T2 extends Comparable<? super T2>>

    This description says:

    I am a Pair class that works with two parameters, that each of them can be compared with classes that deliver from them.

    I hope this helps you out with generics ;-).