What is the purpose of the super command when used in the parameter declaration of a method?

6

In Java the command super has the function of calling the superclass constructor. However, in the declaration of method forEach() of class ArrayList it is used in a different way than usual, see the signature of the method that contains the command super :

public void forEach(Consumer<? super E> action)

The above syntax of the forEach() method has confused me regarding the use of super and I would like to know what is the purpose of it when it is used in this way as quoted above?

    
asked by anonymous 12.03.2016 / 02:45

2 answers

8

This is part of the concept of contravariance applied to the generic concept of Java.

Generic Covariance

Just to contextualize, covariance occurs when we use extends and we allow a more specific type (subclass) to be used instead of a more generic type.

Let's look at the example of a covariant method:

java.util.ArrayList#addAll(java.util.Collection<? extends E>)

Now suppose the following class hierarchy:

class Animal { }
class Gato extends Animal { }
class Cachorro extends Animal { }

If we have a list of Animal , we can add lists of any subtype:

List<Gato> gatos = new ArrayList<>();
List<Cachorro> cachorros = new ArrayList<>();

List<Animal> animais = new ArrayList<>();
animais.addAll(gatos);
animais.addAll(cachorros);

Covariance is by far the easiest to understand and most used.

Generic contravariance

Occurs when we use super and allow a more generic type (superclass) to be used instead of a more specific type. It's practically the opposite of covariance.

Let's look at the example of contravariance quoted in the question:

java.util.ArrayList#forEach(Consumer<? super E> action)

Now suppose the following class hierarchy:

class Animal { 
    void darBanho() { }        
}
class Gato extends Animal { }
class Cachorro extends Animal { }

The idea here is to be able to get a list of Cachorro or a list of Gato both to receive Consumer<Animal> .

Therefore, the goal of contravariance applied to generics is to enable the reuse of generic code.

Example:

List<Gato> gatos = new ArrayList<>();
List<Cachorro> cachorros = new ArrayList<>();

gatos.forEach(Animal::darBanho);   
cachorros.forEach(Animal::darBanho);  

Another example:

List<Animal> animais = new ArrayList<>();
List<Gato> gatos = new ArrayList<>();
List<Cachorro> cachorros = new ArrayList<>();

Collections.copy(animais, gatos);
Collections.copy(animais, cachorros);

In the example above, the copy method has the following signature:

void copy(List<? super T> dest, List<? extends T> src)

In other words: the target list ( dest ) can be of any generic type that is a superclass of the generic type of origin ( src ). In this case, Animal is superclass of Gato and Cachorro .

So the use of super in the method reinforces that the destination list can always receive elements from the source list since you can always assign a specific type to a more generic type.

Contravariance is also somewhat counterintuitive, but it is easier to understand if you think it is often interesting to treat an object or collection of objects as its more generic type for some kind of generic processing.

    
14.03.2016 / 02:21
2

It means that any superclass of E will be accepted.

Examples:

List<? super Integer> foo1 = new ArrayList<Integer>();  // Integer é superclasse de Integer
List<? super Integer> foo2 = new ArrayList<Number>();   // Number é superclasse de Integer
List<? super Integer> foo3 = new ArrayList<Object>(); // Object é superclasse de integer.
    
12.03.2016 / 03:16