18

I'm trying to write a generic max function that takes two Comparables.

So far I have

public static <T extends Comparable<?>> T max(T a, T b) {
    if (a == null) {
        if (b == null) return a;
        else return b;
    }
    if (b == null)
        return a;
    return a.compareTo(b) > 0 ? a : b;
}

This fails to compiles with

The method compareTo(capture#5-of ?) in the type Comparable<capture#5-of ?> is not applicable for the arguments (T)

What I think this is saying is that that the ? in Comparable<?> may be interpreted as one type for parameter a, and another for parameter b, so that they can't be compared.

How do I dig myself out of this hole?

Duncan McGregor
  • 17,665
  • 12
  • 64
  • 118
  • In the `if(a==null)` block, there is no need for the nested if clause. Reducing the block to `return b;` yields the same result (`null` when `b` is `null`, `b` otherwise). – benjamin Nov 28 '14 at 07:37
  • See [org.apache.commons.lang3.ObjectUtils.compare](https://commons.apache.org/proper/commons-lang/javadocs/api-3.6/org/apache/commons/lang3/ObjectUtils.html#compare-T-T-) – Hollis Waite Oct 07 '17 at 02:34

5 Answers5

29

For best results you should use public static <T extends Comparable<? super T>> T max(T a, T b).

The problem with <T extends Comparable<?>> is that this says that the type T is comparable to some type, but you don't know what that type is. Of course, common sense would dictate that a class which implements Comparable should be able to be comparable to at least itself (i.e. be able to compare to objects of its own type), but there is technically nothing preventing class A from implementing Comparable<B>, where A and B have nothing to do with each other. <T extends Comparable<T>> solves this problem.

But there's a subtle problem with that. Suppose class X implements Comparable<X>, and I have a class Y that extends X. So class Y automatically implements Comparable<X> by inheritance. Class Y can't also implement Comparable<Y> because a class cannot implement an interface twice with different type parameters. This is not really a problem, since instances of Y are instances of X, so Y is comparable to all instances of Y. But the problem is that you cannot use the type Y with your <T extends Comparable<T>> T max(T a, T b) function, because Y doesn't implement Comparable<Y>. The bounds are too strict. <T extends Comparable<? super T>> fixes the problem, because it is sufficient for T to be comparable to some supertype of T (which would include all T instances). Recall the rule PECS - producer extends, consumer super - in this case, Comparable is a consumer (it takes in an object to compare against), so super makes sense.

This is the type bounds used by all of the sorting and ordering functions in the Java library.

newacct
  • 119,665
  • 29
  • 163
  • 224
4

You get this error because Comparable<?> basically says that it is comparable to something without any specifics. You should write Comparable<T> instead, so the compiler would know that type T is comparable to itself.

Malcolm
  • 41,014
  • 11
  • 68
  • 91
1

It's offten better getting already implemented iso create owns. See at Min / Max function with two Comparable. Simplest is org.apache.commons.lang.ObjectUtils:

Comparable<C> a = ...;
Comparable<C> b = ...;
Comparable<C> min = ObjectUtils.min(a, b);
Grigory Kislin
  • 16,647
  • 10
  • 125
  • 197
1

Answering my own question from SO's generated related links - this seems to be a subtle duplicate of Fun with Java generics , although I guess you can't blame me for not finding it given the title!

The simplest solution seems to be

public static <T extends Comparable<T>> T max(T a, T b) {
    if (a == null) {
        if (b == null) return a;
        else return b;
    }
    if (b == null)
        return a;
    return a.compareTo(b) > 0 ? a : b;
}
Community
  • 1
  • 1
Duncan McGregor
  • 17,665
  • 12
  • 64
  • 118
1

I've written a utility class for this. Maybe you find it useful (the library is Open Source):

http://softsmithy.sourceforge.net/lib/docs/api/org/softsmithy/lib/util/Comparables.html

Homepage:

http://www.softsmithy.org

Download:

http://sourceforge.net/projects/softsmithy/files/softsmithy/

Maven:

<dependency>  
    <groupid>org.softsmithy.lib</groupid>  
    <artifactid>lib-core</artifactid>  
    <version>0.1</version>  
</dependency> 
Puce
  • 37,247
  • 13
  • 80
  • 152
  • Cool, thanks. This is the sort of thing that I'm gobsmacked isn't part of the standard library, given how much code it takes and how easy it is to get wrong. – Duncan McGregor Jun 23 '11 at 10:15
  • Note: I just checked the source code: it doesn't support null values currently: http://softsmithy.hg.sourceforge.net/hgweb/softsmithy/lib/main-golden/file/5c4db802573b/lib-core/src/main/java/org/softsmithy/lib/util/Comparables.java – Puce Jun 23 '11 at 10:18
  • :-) Thanks for coming clean - I'm sure mine wouldn't have had I not been dealing with a domain where lots of dates might not have been supplied. – Duncan McGregor Jun 23 '11 at 10:22