8

I have a small program that is supposed to sort a map based on its values. Here is what I have so far:

    public static <K, V extends Comparable< ? extends V>> Map<K, V> 
    sortByValues(final Map <K, V> mapToSort)
    {
        List<Map.Entry<K, V>> entries = 
            new ArrayList<Map.Entry<K, V>>(mapToSort.size());

        entries.addAll(mapToSort.entrySet());

        Collections.sort(entries, new Comparator<Map.Entry<K, V>>()
        {
            public int compare(
                               final Map.Entry<K, V> entry1, 
                               final Map.Entry<K, V> entry2)
            {
                return entry1.getValue().compareTo(entry2.getValue());
            }
        });

        Map<K, V> sortedMap = new LinkedHashMap<K, V>();

        for (Map.Entry<K, V> entry : entries)
        {
            sortedMap.put(entry.getKey(), entry.getValue());
        }

        return sortedMap; 
    }

I want my generic value V to be comparable to anything that is either V or is a at least a subclass of V.

I get the following error for the code piece :

public static <K, V extends Comparable< ? extends V>>

Bound mismatch: The method compareTo(? extends V) of type V is not applicable for the arguments (V). The wildcard parameter ? extends V has no lower bound, and may actually be more restrictive than argument V

How can it be more restrictive?

If I change the declaration to:

public static <K, V extends Comparable< ? super V>>

then there is no error. But this is not what I want.

One workaround I have is that, I can change the declaration to:

public static <K, V extends Comparable<V>>

but doing this I lose the flexibility in that I cannot pass a Map whose value implements Comparable with a subclass of itself.

Apologies for such a long question. Thanks in advance.

Swaranga Sarma
  • 13,055
  • 19
  • 60
  • 93
  • Which version of JDK are you using? In JDK5 and JDK6, I don't think this should be a problem. I'm not sure how JDK1.4 will handle this. But, compareTo method doesn't seem to compile like this. It looks like some problem with the way compareTo is handled internally. You may have to change the it to `return ((Comparable)entry1.getValue()).compareTo(entry2.getValue());` or `return ((Comparable)entry1.getValue()).compareTo(entry2.getValue());` – Raze Jun 09 '11 at 11:20
  • I think I've got what you meant. – Raze Jun 09 '11 at 11:32
  • @Raze. I am using JDK 6 Update 21. If it were not for JDK 5 or 6, generics wouldn't compile right? :) – Swaranga Sarma Jun 09 '11 at 12:29
  • Yes, you're right. I some how thought generics were introduced in 1.4, must be because the specification request for generics was JSR 14. – Raze Jun 09 '11 at 14:09
  • You can look here: http://stackoverflow.com/questions/2231804/java-interface-extends-comparable – Nik Jun 09 '11 at 09:35
  • Similar: http://stackoverflow.com/questions/2231804/java-interface-extends-comparable?lq=1 – Mr_and_Mrs_D Apr 08 '14 at 23:10

3 Answers3

4

I think your second option, namely

public static <K, V extends Comparable<? super V>>

is the way to. I think this is so because when you write

public static <K, V extends Comparable<C extends V>>

you basically say that you want to be able to compare any instance of V to any instance of C. But what is missing here is, that because you want to call Collections.sort(..) internally, you also must be able to compare any instance of C to any instance of V. But the generics do not express this, and rightfully the compiler complains.

Basically, to sort some values (at least using Collections.sort(..)) they must be mutually comparable, but the generic restrictions you envision only guarantee that you can compare in one direction.

Waldheinz
  • 10,399
  • 3
  • 31
  • 61
3

implements Comparable with a subclass of itself.

This is "poor design": As a good general rule, classes should not know about their subclasses.

What use case do you have in mind?

I think public static <K, V extends Comparable<V>> is fine.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
0

I think you wanted V to be an extension of some type T. Try adding a temporary type T, as follows in the template declaration:

public static <K, T extends Comparable<T>, V extends T> Map<K, V> 
    sortByValues(final Map <K, V> mapToSort)

everything else remains the same. If you didn't mean that, that is all your Comparables are going to be a sub type of V, then public static <K, V extends Comparable<V>> makes sense, because from V's point of view, the child class of V that overrides compareTo is going to call the compareTo method with the same signature as the compareTo method that V has. Ie, The compareTo method that V declares is what is going to be called (but it may be the overridden definition in V's child class). And so, V extends Comparable<V> is sufficient and correct. I hope I've made myself clear.

Raze
  • 2,175
  • 14
  • 30