3

I have the following interface:

public interface ISort<T extends Comparable<T>> {
    public void sort(T[] array);
}

My understanding of this is that the the <T extends Comparable<T>> tells the compiler that within this class, there may be some generic types T, and they must be a Comparable<T> or be any class which implements Comparable<T>.

I then have the following class:

public class Sort<T extends Comparable<T>> implements ISort<T> {

    public void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    public void sort(T[] array) {
        java.util.Arrays.sort(array);
    }

}

Again, I have the <T extends Comparable<T>> telling the compiler that within this class I will be using the type T which must form an IS-A relationship with Comparable<T>. However, why must I only type implements ISort<T>, why do I not need to write implements ISort<T extends Comparable<T>>? In order to help me understand this could you explain what exactly these generics statements are inferring to the compiler?

Keir Simmons
  • 1,634
  • 7
  • 21
  • 37

3 Answers3

3

There is a difference between specifying T with the class and using T in the extends or implements clause.

When specifying T with the class, you are declaring the type parameter T, but in the extends or implements clause, you are using a type parameter that is already declared.

public class Sort<T extends Comparable<T>>  // Declare T to be Comparable<T>
    implements ISort<T>                     // Use T

Using a type parameter in the extends or implements clause is no different than using the type parameter in the body of the class.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • This explanation makes the most sense to me. However, I still have a few questions. If I am simply using `T` in the `implements ...` part, that means we have already ensured that `T`'s upper bound is `Comparable`. If that's the case, then why do I need to specify `T extends Comparable` in the interface file (my first code snippet)? – Keir Simmons Aug 26 '14 at 22:35
  • Each class/interface can define its own type parameters. `ISort` is declaring its own `T` to be `Comparable`, and `Sort` is declaring its own `T` to be `Comparable` also. The implements clause on `Sort` indicates that `Sort` is supplying its own `T` as `ISort`'s type parameter. – rgettman Aug 26 '14 at 22:38
2

The syntax for generic type usage is SomeType<SomeTypeArgument>.

In your case

class Sort<T extends Comparable<T>> implements ISort<T> {
        // ^ declares new type parameter             ^ uses it as a type argument 
        //                             

You declare a new type parameter T with bounds that match the expectations in the type parameter T declared in ISort. You can therefore use Sort#T as a type argument for ISort.

This would be similar to

class Sort implements ISort<Integer> {

The only difference is you don't declare a type parameter, you use an existing type.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
0

In the class declaration you introduce a type parameter with an upper bound. When you use T[] as the type of the first parameter of the sort method, you are not declaring anything (the declaration happens at the beginning of the file): you are merely using a type variable. Please, note the different phrasing:

  • at the beginning of the file, you declare a type parameter
  • in the signature of sort, we use a type variable

Bounds on type parameters are only allowed at declaration site, and the declaration can happen in three places:

  1. class declaration
  2. constructor definition
  3. method definition

Here you a quick table:

// class
public static class Foo<T> {
    // constructor
    public <A> Foo() {}
    // method
    public <B> void doSomething() {}
    // Not really allowed
    // public void doSomethingElse(List<T extends Number> bar) {}
    // here we have a wildcard, so you can do this
    public void doSomethingElse(List<? super T> bar) {}
}
Raffaele
  • 20,627
  • 6
  • 47
  • 86