Wild card in java Generic and <? super T> meaning, lower or upper bound

1.4k views Asked by At

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!

4

There are 4 answers

3
Bohemian On BEST ANSWER

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.

2
Maxim Shoustin On

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);
0
René Link On

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.

1
newacct On

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