Question

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!

Was it helpful?

Solution

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.

OTHER TIPS

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

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.

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);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top