"Hi! I'm going to continue Ellie's lesson on generics. Ready to listen?"

"Yes."

"Then let's begin."

"The first thing you need to know is that a class's methods can also have their own type parameters."

"I know."

"No, I specifically mean their own type parameters:"

Example
class Calculator
{
  T add(T a, T b); // Add
  T sub(T a, T b); // Subtract
  T mul(T a, T b); // Multiply
  T div(T a, T b); // Divide
}

"These type parameters pertain specifically to the methods. The class doesn't have parameters. You could even declare these methods static and call them without an object."

"I see. The point of the type parameters in methods is the same as for classes?"

"Yes. But there's something new."

"As you already know, you can use a wildcard in the type declaration. Then imagine the following situation:"

Example 1
public void doSomething(List<? extends MyClass> list)
{
 for(MyClass object : list)
 {
  System.out.println(object.getState()); // Everything works well here.
 }
}

"But what if we want to add a new item to the collection:"

Example 2
public void doSomething(List<? extends MyClass> list)
{
 list.add(new MyClass()); // Error!
}

"The problem is that in the general case the doSomething method may be passed a List whose elements are not MyClass objects, but rather objects of any subclass of MyClass. But you can't add MyClass objects to such a list!"

"Ah. So, what can be done about that?"

"Nothing. In this situation, you can't do anything. But this gave Java's creators something to think about. And they came up with a new keyword: super."

"The syntax looks almost the same:"

List<? super MyClass> list

But there's an important distinction between extends and super.

"«? extends T» means that the class must be a descendant of T."

"«? super T» means that the class must be a ancestor of T."

"Holy moly. So where is this used?"

"«? super T» is used when a method involves adding to a collection of T objects. In this case, it could be a collection of T objects or any ancestor of T."

"Ah. A T object can be assigned to a reference variable whose type is any of T's ancestors"

"Honestly, this approach is not used very often. What's more, it has a shortcoming. For example:"

Examples
public void doSomething(List<? super MyClass> list)
{
 for(MyClass object : list) // Error!
 {
  System.out.println(object.getState());
 }
}
public void doSomething(List<? super MyClass> list)
{
 list.add(new MyClass()); // Everything works well here.
}

"Now the first example doesn't work."

"Since list could even be a List<Object> (Object is MyClass's topmost superclass), we're essentially writing the following invalid code:"

Example 1
List<Object> list;

for(MyClass object : list) // Error!
{
 System.out.println(object.getState());
}

"I see. Thanks for the interesting lesson."

"You're welcome."