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

4

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!

Dover answered 10/12, 2013 at 15:3 Comment(5)
This would help you. thejavageek.com/2013/08/28/generics-the-wildcard-operatorPesticide
This should be usefull for you #4343702Gouda
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.Nephology
@PrasadKharkar thanks a lot! The blog is ready good and it even have a example in it!Dover
"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[].Hereditament
S
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.

Sandglass answered 10/12, 2013 at 15:12 Comment(3)
but who said T was Truck? If you choose T to be Vehicle, then it works.Hereditament
@Hereditament 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.Sandglass
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.Hereditament
C
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);
Chilson answered 10/12, 2013 at 15:7 Comment(2)
Correct me if I am wrong.. So "? super T" could be either a upper bound or a lower bound, depending on where I put this phrase, right?Dover
The super is a lower bound, when extends is an upper boundChilson
C
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.

Calle answered 10/12, 2013 at 15:22 Comment(0)
H
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);
Hereditament answered 13/12, 2013 at 10:29 Comment(1)
+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<T> instead, Comparator<? super T> would be needed.Cranach

© 2022 - 2024 — McMap. All rights reserved.