Java- the Meaning of <T Extends Comparable<T>>

Java- The meaning of T extends ComparableT?

This means that the type parameter must support comparison with other instances of its own type, via the Comparable interface.

An example of such a class is provided in the Oracle tutorial Object Ordering. Note the similar pattern to T extends Comparable<T> in the excerpt below:

public class Name implements Comparable<Name> {
...
public int compareTo(Name n) { ... }
}

What does T extends ComparableT mean?

First, you have the Comparable interface which roughly looks like:

public interface Comparable<T> {

int compareTo(T other);
}

As you can see, the type parameter of Comparable is used as the parameter of the compareTo method. Typically, the type argument for T is the same class which is implementing the Comparable interface. This generic setup facilitates comparing instances of the same type with each other. Here's an example:

public class Name implements Comparable<Name> {

@Override
public int compareTo(Name other) {
// compute & return result
}
}

Now let's say you have a method which should return the maximum of two objects according to their natural order. Such a method could look like the following:

public static <U extends Comparable<U>> U max(U left, U right) {
return left.compareTo(right) >= 0 ? left : right;
}

Note: Used U as the type variable instead of T to show it is separate from the T used in the Comparable interface.

The above is a generic method. The type variable U is upper-bounded by Comparable<U>. What this means is that the type argument used in place of U must be assignable to (i.e. subtype of) Comparable<U>. For instance, if we use Name as the type argument it will work because Name is assignable to Comparable<Name>. The reason for specifying the upper-bound as Comparable<U> is that the method needs to call compareTo in order to function properly.

Name name1 = ...;
Name name2 = ...;

Name max = max(name1, name2); // U is inferred to be Name

As shown above, having U as the return type also allows assigning the result to a variable of the same type as the arguments.


Note that for maximum flexibility the max method should actually be declared like so:

public static <U extends Comparable<? super U>> U max(U left, U right) {
return left.compareTo(right) >= 0 ? left : right;
}

The difference being the use of Comparable<? super U> as the upper-bound instead of Comparable<U>. These two Q&As should help explain why using ? super U offers greater flexibility:

  • What is PECS (Producer Extends Consumer Super)?
  • Explanation of generic <T extends Comparable<? super T>> in collection.sort/ comparable code?

Explanation of generic T extends Comparable? super T in collection.sort/ comparable code?

Actually, it means that T can implement Comparable<? super T>, not just Comparable<T>.

For example, it means that a Student class can implement Comparable<Person>, where Student is a subclass of Person:

public class Person {}

public class Student extends Person implements Comparable<Person> {
@Override public int compareTo(Person that) {
// ...
}
}

In this case, a List can be sorted by Collections.sort() but only based on Person's properties, because you pass the Student instance into compareTo() as a Person (unless you downcast it, of course).

In practice however, you'll never see a Student class implement Comparable<Person>. That's because Person will probably have implemented Comparable<Person>, and Student inherits it implementation. The end result is the same however: you can pass a List<Student> to Collections.sort() and have it sorted on Person's properties.

The difference between Comparable<T> and Comparable<? super T> is more obvious in the overloaded version of Collections.sort() that takes a Comparator<? super T>:

class ByAgeAscending implements Comparator<Person> {
@Override public int compare(Person a, Person b) {
return a.getAge() < b.getAge();
}
}

List<Student> students = getSomeStudents();
Collections.sort(students, new ByAgeAscending());

Why is it T extends Comparable and not T implements Comparable?

Simply put: Java does not make a distinction between interfaces and classes when defining bounds for generic type parameters. extends is used for both interface and class types which makes expressing the bound direction (link added from Sambits comment on the main post) more streamlined.

Difference between generics T extends Number & ComparableT and T extends Comparable? extends Number

You won't be able to use:

Comparable<? extends Number>

because the only methods defined by Comparable are consumers (in the sense of PECS): it needs to accept instances of type ? extends Number into its compareTo method - and there is no type which satisfies that bound safely.

Integer is a Comparable<? extends Number>, but so is Double. Thus, you can't safely call instance1.compareTo(instance2) because it would fail if these instances are concretely Integer and Double respectively, since Integer.compareTo can only accept Integer parameters.

As such, the compiler prevents you from calling this method in the first place.

Comparable VS ? extends Comparable

This happens because generics are invariant. Even if String is a Comparable, meaning:

String s = "";
Comparable c = s; // would work

Generics of these would not work:

List<Comparable> listC = List.of();
List<String> listS = List.of();

listC = listS; // will fail

And this would not work no matter what is the relationship between Comparable and String.

When you change the definition of that method to:

public static boolean func(List<? extends Comparable> lst) {
...
}

This is said that: wildcard with an extends-bound makes the type covariant.

This means that :

List<? extends Comparable> listC = List.of();
List<String> listS = List.of();

listC = listS; // would work here

Or in simpler words it means that List<String> is a subtype of List<? extends Comparable>.

There is a small price to pay now, because listC is now a producer of elements, meaning you can take elements out of it, but you can not put anything into it.

And well after you understand this, you are not done yet, because the definition of that method would be entirely correct, when written like this:

 public static <T extends Comparable<? super T>> boolean func(List<T> lst) {
.....
}

Why does T extends Comparable ? super T include T? Meaning includes ComparableT?

From Lower Bounded Wildcards, a section of the Generics section of The Java Tutorial:

... a lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type.

(bold mine, emphasis theirs)

Thus, the set of classes that match T extends Comparable<T> is a subset of the set of classes that match T extends Comparable<? super T>. The wildcard ? super T itself matches T and any superclasses of T.

In other words, the assumption that "super would only include super class items" is simply incorrect.

Your confusion in the example may also arise from the fact that JTextField is a superclass of JPasswordField; in other words, JPasswordField extends JTextField. The example would match any of the following classes:

  • javax.swing.JPasswordField
  • javax.swing.JTextField
  • javax.swing.JTextComponent
  • javax.swing.JComponent
  • java.awt.Container
  • java.awt.Component
  • java.lang.Object

The example would make much more sense as the following:

void describeComponent(CustomComponent<? super JTextField> ref) {...}

Note that JPasswordField, which is a subclass of JTextField, itself is
omitted in the list of permissible objects, because it is not a
superclass of JTextField.

Difference between T extends ComparableT and T extends ComparableComparableT?

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

How come T extends ComparableT working as T extends Comparable? super T?

You compile with Java 8 I think.
Because, before Java 8, you should not pass the compilation and have this error :

Bound mismatch: The generic method function(T) of type Main is not
applicable for the arguments (Apple). The inferred type Apple is not a
valid substitute for the bounded parameter >

I think what you read in your book refers to generic use before Java 8.

But i didn't know that Java 8 had less constraint on this kind of case.

Is it a side-effect of the large use of inference in Java 8 that Java 8 calls the "improved inference" ?

Problem with T extends Comparable? super T

Suppose we changed the max method to this:

<T extends Comparable<T>> T max(Collection<? extends T> coll)

You would not be able to get the max of a List<Apple> because Apple does not implement Comparable<Apple>, it implements Comparable<Fruit>. But you and I know perfectly well that an Apple knows how to compare itself to another Fruit because it inherited that functionality.

We fix the problem by changing the declaration of max to this:

<T extends Comparable<? super T>> T max(Collection<? extends T> coll)

This means that we accept any class T such that:

  1. T implements Comparable<T>, or...
  2. T implements Comparable<X> for some X such that X is a super class of T

In order to find the max, we must be sure that any instance of T can safely accept another instance of T as an argument to its compare method.

In the first scenario, it is obvious that any instance of T can safely accept another instance of T as an argument to its compare(T) method.

In the second scenario, any instance of T can safely accept another instance of T as an argument to its compare(X) method because all instances of T are also instances of X.

Your example illustrates the second scenario, where T corresponds to Apple and X corresponds to Fruit.



Related Topics



Leave a reply



Submit