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:
T implements Comparable<T>
, or...T implements Comparable<X>
for someX
such thatX
is a super class ofT
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
How to Call Methods in Constructor in Java
How to Create a New Packaging Type for Maven
Java Import VS Code Performance
Apache Commons Equals/Hashcode Builder
Parsing Nested JSON Data Using Gson
How to Use Sqoop in Java Program
Try with Resources Introduce Unreachable Bytecode
Why Can Array Constants Only Be Used in Initializers
How to Execute .SQL Script File Using Jdbc
How to Pass Parameter to Jsp:Include via C:Set? What Are the Scopes of the Variables in Jsp
How to Make a List with Checkboxes in Java Swing
When Does Java's Thread.Sleep Throw Interruptedexception
Getting Enum Associated with Int Value
In Java, Differencebetween This.Method() and Method()