简体   繁体   中英

Difference between “<T extends Comparable<T>>” and “<T extends Comparable<Comparable<T>>>”?

In the expression <T extends Comparable<T>> in a signature like

public static <T extends Comparable<T>> foo(T x) { ... }

the description of T depends recursively on Comparable<T> .

If T extends Comparable<T> , and Comparable<T> extends Comparable<Comparable<T>> , doesn't it follow that T extends Comparable<Comparable<T>> ?

IOW, is the extends relationship transitive?

If so, why isn't

public static <T extends Comparable<Comparable<T>>> int compare(T x, T y) {
    return x.compareTo(y);
}

equivalent to

public static <T extends Comparable<T>> int compare(T x, T y) {
    return x.compareTo(y);
}

? In fact, the last definition compiles, whereas the one above it fails to compile, with the error

comp.java:7: compareTo(java.lang.Comparable<T>) in 
java.lang.Comparable<java.lang.Comparable<T>> cannot be applied to (T)
            return x.compareTo(y);

Thanks!

You've made an incorrect logical jump here, I think.

T extends Comparable<T>

does not imply that

Comparable<T> extends Comparable<Comparable<T>>

Short analogy :

Suppose a rock-crusher is any material that can crush rock , and rock just happens to be capable of crushing itself. So rock is a rock-crusher. Does this mean that rock can crush any substance which can crush rock? Clearly not: steel might also be a rock-crusher while rock might not crush steel.


Long (code-based) analogy :

Suppose I have an interface, Additive<T> , which can be used to describe any type to which you may perform an add operation on a T object and get back a T .

Now let's say I have some method whose signature looks like this:

public static <T extends Additive<T>> add(T x, T y) { ... }

Since x must be of a type to which I can add a T , this method could be implemented as follows:

return x.add(y);

Make sense? OK, great; but now let's make the assumption you've made in your question: that Additive<T> must extend Additive<Additive<T>> ; if this is the case then I can assume that I can add to x an instance of any type implementing Additive<T> .

Here's where the assumption breaks down. I might have a type, say...

// A 32-bit integral number supporting addition
public class Int32 implements Additive<Int32> { ... }

That would make sense, right? I could also have something like this:

// A list of 32-bit integral numbers supporting appending
public class Int32List implements Additive<Int32> { ... }

Now, if T extends Additive<T> implied that Additive<T> extends Additive<Additive<T>> then I should be able to do this:

Additive<Int32> x = new Int32(5);
Additive<Int32> y = x.add(new Int32List());

Does that make any sense?

There's nothing special about

<T extends Comparable<T>>

Consider

public class Foo implements Comparable<Foo>{
     int compareTo(Foo x);
}

I don't even think we can say that it's defined recursively, it's just self referential.

Comparable<T> extends Comparable<Comparable<T>>

Perhaps I misunderstand what relationship you are conveying, but this statement is untrue. Comparable<T> does not extend anything.

The fundamental issue to keep in mind is that Java generics are scoped by the compilation unit and compile time. It is just syntactic sugar! (How many times I forget this and end up reasoning precisely the way you do, as if Java Generics actually implied a generic type system in runtime .)

From the spec :

The scope of a class' type parameter is the entire declaration of the class 
including the type parameter section itself. 

Therefore, type parameters can appear as parts of their own bounds, or as 
bounds of other type parameters declared in the same section.

There is no recursion, and no new types are defined.

To wit:

package so_6949760;

import java.util.HashMap;

public class SweetCompilingGenerics {

    @SuppressWarnings("serial")
    public static class Sweet<X> extends HashMap<X, Sweet<X>> {}

    public static void main(String[] args) {
        Sweet<String> sweetStr = new Sweet<String>();
        Sweet<Sweet<Integer>> whatever = new Sweet<SweetCompilingGenerics.Sweet<Integer>>();

        assert sweetStr.getClass().equals(whatever.getClass()) : "who made generics a runtime mechanism?";
        assert sweetStr.getClass() == whatever.getClass() : "who made generics a runtime mechanism?";

        System.out.format("Is %s a Sweet<String> ?\n", sweetStr.getClass().getCanonicalName());
        System.out.format("Is %s a Sweet<Sweet<Integer>> ?\n", whatever.getClass().getCanonicalName());
    }
}

Will output the sweet secret:

Is so_6949760.SweetCompilingGenerics.Sweet a Sweet<String> ? 
Is so_6949760.SweetCompilingGenerics.Sweet a Sweet<Sweet<Integer>> ?

May I be so forward to offer an advice to a fellow geek? Always read the specification .

[post accept edit]: Just found this highly informative article by IBM . That pretty much addresses all of OP's questions. (Recommended).

Comparable<T> does not extend Comparable<Comparable<T>> (just because T extends Comparable<T> ) in the same way that List<String> does not extend List<Object> (just because String extends Object ).

Please refer to this document from Oracle to read more about generics and inheritance.

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