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

Asked

Viewed 692 times

7

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

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

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

2 answers

9


That’s part of the concept of countervariance applied to the concept of Java generics.

Covariance in generics

Just to put it in context, covariance occurs when we use extends and allow a more specific type (subclass) to be used in place of a more generic type.

Let’s take 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 far easier to understand and more used.

Contravariance in generics

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

Let’s go to the example of countervariance cited 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 make a list of Cachorro or a list of Gato may both receive a Consumer<Animal>.

Therefore, the objective of countervariance 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 above example, the method copy has the following signature:

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

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

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

Countervariance is also a little counterintuitive, but it’s easier to understand if you think that it’s often interesting to treat an object or collection of objects as your most generic type for some kind of generic processing.

  • Could make a java7 code?

  • @Davidschrammel Done. Look at the second example of countervariance.

3

It means 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.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.