Understanding upper and lower bounds on ? in Java Generics

28,146

Solution 1

? as a type parameter can only be used in methods. eg: printAll(MyList<? extends Serializable>) I cannot define classes with ? as type parameter.

A wildcard (?) isn't a formal type parameter, but rather can be used as a type argument. In the example you give, ? extends Serializable is given as a type argument to the generic type MyList, of the printAll method's parameter.

Methods can also declare type parameters like classes, for example:

static <T extends Serializable> void printAll(MyList<T> myList)

I understand the upper bound on ?. printAll(MyList<? extends Serializable>) means printAll will print MyList if it has objects that implement the Serialzable interface

More accurately, it means a call to printAll will compile only if it is passed a MyList with some generic type that is or implements Serializable. In this case it would accept a MyList<Serializable>, MyList<Integer>, etc.

I have a bit of an issue with the super. printAll(MyList<? super MyClass>) means printAll will print MyList if it has objects of MyClass or any class which extends MyClass (the descendants of MyClass)

A wildcard bounded with super is a lower bound. So we could say a call to printAll will compile only if it is passed a MyList with some generic type that is MyClass or some super-type of MyClass. So in this case it would accept MyList<MyClass>, e.g. MyList<MyParentClass>, or MyList<Object>.

So, say if MyClass looks like:

public class MyClass extends Thread implements ActionListener{
    // whatever
}

then, printAll() will print if

  1. There are objects of MyClass in the list
  2. There are objects of Thread or ActionListener in the list

You're on the right track. But I think saying e.g. "it will print if there are objects of MyClass in the list" is problematic. That makes it sound like you're defining runtime behavior - generics are all about compile time checks. For example wouldn't be able to pass a MyList<MySubclass> as an argument for MyList<? super MyClass>, even though it might contain instances of MyClass, by inheritance. I would reword it to:

A call to printAll(MyList<? super MyClass>) will compile only if it is passed a:

  1. MyList<MyClass>
  2. MyList<Thread>
  3. MyList<Runnable>
  4. MyList<ActionListener>
  5. MyList<EventListener>
  6. MyList<Object>
  7. MyList<? super X> where X is MyClass, Thread, Runnable, ActionListener, EventListener, or Object.

So, after having read the many answers to the question, here is my understanding:

? extends T means any class which extends T. Thus, we are referring to the children of T. Hence, T is the upper bound. The upper-most class in the inheritance hierarchy

? super T means any class / interface which is super of T. Thus we are referring to all the parents of T. T is thus the lower bound. The lower-most class in the inheritance hierarchy

Close, but I wouldn't say "children of T" or "parents of T", since these bounds are inclusive - it would be more accurate to say "T or its subtypes", and "T or its supertypes".

Solution 2

First of all T or E or K or whatever are not fixed names. They are just type variables, and you decide the name for them. T, E, K are just examples but you could call it Foo or whatever.

Now going to your first question: since the wildcard ? represents the "any and unknown" type, the unspecified one, it doesn't make any sense to declare a class generic over an unspecified type. It's useful to have wildcard in parameters of methods or in variables when you don't care about the type.

Now regarding your second question: the lower bound gives even more flexibility to your generic methods. both extends and super are the opposite:

  • ? extends T: an unknown type which is a subtype of T
  • ? super T: an unknown type which is a super type of T

The latter can be useful when you want to accept a type that is compatible with T (so that T is-a that type). A practical example can be found here.

Solution 3

Lets start from the beginning.

Strictly speaking any valid java identifier can be used as a generic type parameter - it is just a special type of variable:

public static final class MyGenericClass<MyGenericType> {

}

Is perfectly valid Java.

Next, you can use ? anywhere where you can make a declaration. You can use the wildcard when you declare variables but not when you instantiate them:

public static final class MyGenericClass {
    private final Collection<? extends String> myThings;

    public MyGenericClass(Collection<? extends String> myThings) {
        this.myThings = myThings;
    }  

    public void doStuff(final Collection<? extends String> myThings) {

    }
}

Is again all valid, you cannot do this:

final Collection<? extends String> myThings = new ArrayList<? extends String>();

When it comes to extends vs super this is called co-variance vs contra-variance. It determines which direction along the class hierarchy supplied types are allowed to travel:

final Collection<? extends Runnable> example1 = new ArrayList<Runnable>();
final Collection<? extends Runnable> example2 = new ArrayList<TimerTask>();
final Collection<? super Runnable> example3 = new ArrayList<Runnable>();
final Collection<? super Runnable> example4 = new ArrayList<Object>();

The first two examples demonstrate extends - the tightest bound you can assume from the Collection is Runnable as the user can pass a Collection of anything that has Runnable in its inheritance hierarchy.

The second two example demonstrate super - the tightest bound you can assume from the Collection is Object as we allow anything that is in the inheritance hierarchy of Runnable.

Solution 4

For the first question: you can not define a method with ? as a type parameter either. The following will not compile:

void <?> foo() {}

? is used for binding to another generics without providing the type parameter. You can write for methods:

void foo(List<?> e) {}

And you can also write for classes:

public class Bar<E extends List<?>> { }

For the use of super:

public void printAll(MyList<? super MyClass>){
    // code code code
}

This will not as you say print the list "if it has objects of MyClass". It can have objects of any class that is a subclass of a class that is a parent of MyClass. The compiler does not know at compile time what are the objects that will be in the list anyway.

To get your head around it, consider a simple example with the Number class hierarchy. Float and Integer are children of Number. You can write your method like this:

public void printAll(List<? super Float>){
    // code code code
}

Then you can call that method with a List<Number>:

List<Number> numbers = new ArrayList<>();
numbers.add(1); // actually only add an Integer
printAll(numbers); // compiles.

This is possible would not be super useful in that case. Where it would be useful for example is when you want to add Float to a collection without wanting it to be only a List, like:

public void addFloat(List<? super Float> list){
    list.add(2.5);
}

Solution 5

Lower-bound says: You can use the class mentioned after 'super' keyword, or any of its super types. This can get tricky. Those super types may have other (completely different) classes inheriting from them, as below example shows.

Say,

List<? super Car> cars = new ArrayList <Vehicle>();

Should the programmer be allowed to write:

cars.add(new Helicopter()); //Helicopter is a kind of Vehicle

That should obviously not be allowed, and it reflects a danger in using lower-bounds.

Programmer should be allowed to add a Vehicle to list, but not ANY Vehicle. He must cast it, to let Java know he is only adding a Car Vehicle after all, like this:

cars.add((Car) new Vehicle()); 
Share:
28,146
An SO User
Author by

An SO User

Updated on November 30, 2020

Comments

  • An SO User
    An SO User over 3 years

    I am really having a tough time understanding the wild card parameter. I have a few questions regarding that.

    1. ? as a type parameter can only be used in methods. eg: printAll(MyList<? extends Serializable>) I cannot define classes with ? as type parameter.

    2. I understand the upper bound on ?. printAll(MyList<? extends Serializable>) means: "printAll will print MyList if it has objects that implement the Serialzable interface."
      I have a bit of an issue with the super. printAll(MyList<? super MyClass>) means: "printAll will print MyList if it has objects of MyClass or any class which extends MyClass (the descendants of MyClass)."

    Correct me where I went wrong.

    In short, only T or E or K or V or N can be used as type parameters for defining generic classes. ? can only be used in methods


    Update 1:

    public void printAll(MyList<? super MyClass>){
        // code code code
    }
    

    Accordint to Ivor Horton's book, MyList<? super MyClass> means that I can print MyList if it has objects of MyClass or any of the interfaces or classes it implements. That is, MyClass is a lower bound. It is the last class in the inheritance hierarchy. This means my initial assumption was wrong.

    So, say if MyClass looks like:

    public class MyClass extends Thread implements ActionListener{
        // whatever
    }
    

    then, printAll() will print if
    1. There are objects of MyClass in the list
    2. There are objects of Thread or ActionListener in the List


    Update 2:

    So, after having read the many answers to the question, here is my understanding:

    1. ? extends T means any class which extends T. Thus, we are referring to the children of T. Hence, T is the upper bound. The upper-most class in the inheritance hierarchy

    2. ? super T means any class / interface which is super of T. Thus we are referring to all the parents of T. T is thus the lower bound. The lower-most class in the inheritance hierarchy