6

So I am reading about generic method and I am get confused. Let me state the problem here first:

In this example: Suppose that I need a version of selectionSort that works for any type T, by using an external comparable supplied by the caller.

First attempt:

public static <T> void selectionSort(T[] arr, Comparator<T> myComparator){....}

Suppose that I have:

  • Defined vehicle class
  • created VehicleComparator implementing Comparator while compare vehicles by their price.
  • created Truck extends vehicle
  • instantiated Truck[] arr ; VehicleComparator myComparator

Now, I do:

selectionSort(arr, myComparator);

and it won't work, because myComparator is not available for any subclass of Vehicle.

Then, I do this:

public static <T> void selectionSort(T[] arr, Comparator<? super T> myComparator){....}

This declaration will work, but I don't completely sure what I've been doing... I know use is the way to go. If "? super T" means "an unknown supertype of T", then am I imposing a upper or lower bound? Why is it super? My intention is to let any subclass of T to use myComparator, why "? super T". So confused... I'd appreciate if you have any insight in this..

Thanks ahead!

dev2d
  • 3,991
  • 2
  • 26
  • 50
Victoria J.
  • 313
  • 2
  • 11
  • 1
    This would help you. http://www.thejavageek.com/2013/08/28/generics-the-wildcard-operator/ – Prasad Kharkar Dec 10 '13 at 15:05
  • This should be usefull for you http://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java – Pawel Dec 10 '13 at 15:10
  • This also might help you. **Effective Java** book suggests to remember this - **PECS (Producer `extends`, Consumer `super`)**. Use `extends` for producing classes, and use `super` for consumer classes. – KrishPrabakar Dec 10 '13 at 15:11
  • @PrasadKharkar thanks a lot! The blog is ready good and it even have a example in it! – Victoria J. Dec 10 '13 at 15:21
  • "and it won't work, because myComparator is not available for any subclass of Vehicle." This is false. `myComparator` compares `Vehicle`, yes. And `Truck[]` is a `Vehicle[]`. – newacct Dec 11 '13 at 01:37

4 Answers4

5

Firstly, you could have solved it by having Vehicle[] which you then added Trucks to.

The reason you need <? super T> goes back to the generics rule that Comparator<Truck> is not a subtype of Comparator<Vehicle>; the unbounded type T must match exactly, which it doesn't.

In order for a suitable Comparator to be passed in, it must be a Comparator of the class being compared or any super class of it, because in OO languages any class may be treated as an instance of a superclass. Thus, it doesn't matter what the generic type of the Comparator is, as long as it's a supertype of the array's component type.

Paul Bellora
  • 51,514
  • 17
  • 127
  • 176
Bohemian
  • 365,064
  • 84
  • 522
  • 658
  • but who said `T` was `Truck`? If you choose `T` to be `Vehicle`, then it works. – newacct Dec 13 '13 at 10:26
  • @newacct if you read the wuestion carefully, you'll see that he instantiated `Truck[] arr`, then passed that in to `T[]`, so java inferred `T` as `Truck`. There's no doubt about it. – Bohemian Dec 13 '13 at 11:45
  • 1
    Sure, his compiler may have inferred it that way. But if the inference is insufficient he can always manually provide the type argument. You said "you *need* ` super T>`" which is not true since it can work without it. – newacct Dec 13 '13 at 23:02
4

The quizzical phrase ? super T means that the destination list may have elements of any type that is a supertype of T, just as the source list may have elements of any type that is a subtype of T.

We can see pretty simple example copy from Collections:

public static <T> void copy(List<? super T> dst, List<? extends T> src) {
   for (int i = 0; i < src.size(); i++) {
    dst.set(i, src.get(i));
   }
}

And call:

List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints);
assert objs.toString().equals("[5, 6, four]");

As with any generic method, the type parameter may be inferred or may be given explicitly. In this case, there are four possible choices, all of which type-check and all of which have the same effect:

Collections.copy(objs, ints);
Collections.<Object>copy(objs, ints);
Collections.<Number>copy(objs, ints);
Collections.<Integer>copy(objs, ints);
Maxim Shoustin
  • 76,444
  • 28
  • 192
  • 219
2

Your method signature

public static <T> void selectionSort(T[] arr, Comparator<? super T> myComparator)

means that if you invoke it with an array of type T than you must also provide a Comparator of type T or a super type of T.

For example if you have the following classes

class Vehicle {}

class Truck extends Vehicle {}

class BigTruck extends Truck {}

class VehicleComparator implements Comparator<Vehicle> {    
    public int compare(Vehicle o1, Vehicle o2) {
        return 0;
    }
}

class BigTruckComparator implements Comparator<BigTruck> {
    public int compare(BigTruck o1, BigTruck o2) {
        return 0;
    }
}

class TruckComparator implements Comparator<Truck> {
    public int compare(Truck o1, Truck o2) {
        return 0;
    }
}

then this will work

Truck[] trucks = ...;
selectionSort(trucks, new TruckComparator());
selectionSort(trucks, new VehicleComparator());

Because

  • TruckComparator implements Comparator<Truck> and a Truck is equal to the array's type Truck
  • VehicleComparator implements Comparator<Vehicle> and a Vehicle is a super type of the array's type Truck

This will NOT WORK

selectionSort(trucks, new BigTruckComparator());

Because a BigTruckComparator is a Comparator<BigTruck> and a BigTruck is not a super type of the array's type Truck.

Matilda Smeds
  • 1,026
  • 9
  • 17
René Link
  • 38,761
  • 11
  • 84
  • 115
1

The two signatures are equivalent in terms of power -- for any set of arguments, if there exists a choice of type arguments that works for one of them, there exists a choice of type arguments that works for the other one, and vice versa.

You are simply running into limited inference in your compiler. Simply explicitly specify the desired type argument:

YourClass.<Vehicle>selectionSort(arr, myComparator);
newacct
  • 110,405
  • 27
  • 152
  • 217
  • +1 Though it may be worth mentioning that this is only true because arrays are covariant (`Truck[]` is a `Vehicle[]`). If the method took a `List` instead, `Comparator super T>` would be needed. – Paul Bellora Dec 24 '13 at 03:35