简体   繁体   中英

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()

I would declare my class as such:

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

Meaning that objects are comparable with objects of the same type as they are (your error means that the compiler cannot make sure objects are compared to other objects of the same type).


Your code with my edits compiles properly:

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

    public T1 getKey() {
        return key;
    }

    public T2 getValue() {
        return value;
    }

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

    public static void test() {
        Pair<String, Integer> p1 = new Pair<String, Integer>();
        Pair<String, Integer> p2 = new Pair<String, Integer>();

        p1.KEY_COMPARATOR.compare(p1, p2);
    }
}

You should however make a separate class (or a static final class) of the comparator so that it is more intuitive to use & also does not increase the weight of each Pair instance.

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 ;-).

if you knew the differences between:

List<Object>

and

List<String>

... it will be easier. basically, they are two different types in java (not "same" List type as you think).

The generic type "logic" is not very straightforward as you think in your mind or as it appears to be.

I think you should declare your "Pair" class as the following:

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

since you have already made the "T1" T2" comparable, why could you want to implement a Comparator?

if you want to use the "Collections.sort(myCollection, myComparator)", then you don't have to declare that "T1" and "T2" is "comparable"... just make sure your "KEY_COMPARATOR" accepts them...

either way, there is redundancy in the code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM