Comment contravariance serait-elle utilisée dans les génériques Java?

En Java, la covariance permet au concepteur D'API de spécifier qu'une instance peut être généralisée comme un certain type ou l'un des sous-types de ce type. Par exemple:

List<? extends Shape> shapes = new ArrayList<Circle>(); 
// where type Circle extends Shape

Contravariance va dans l'autre sens. Il nous permet de spécifier qu'une instance peut être généralisée comme un certain type ou supertype.

List<? super Shape> shapes = new ArrayList<Geometry>();
// where Shape extends Geometry

Comment la contravariance de Java generic est-elle utile? Quand vous choisissez de les utiliser?

23
demandé sur Peter Mortensen 2010-10-05 09:53:59

3 réponses

Eh Bien, votre deuxième exemple vous permettent d'écrire:

Shape shape = getShapeFromSomewhere();
shapes.add(shape);

Alors que vous ne pouviez pas le faire avec le premier formulaire. Ce n'est pas utile aussi souvent que la covariance, je vous l'accorde.

, Un domaine où il peut être utile, c'est en termes de comparaisons. Par exemple, considérons:

class AreaComparer implements Comparator<Shape>
...

Vous pouvez utiliser pour comparer tout deux formes... donc ce serait bien si nous pouvions aussi utiliser pour trier un List<Circle>, par exemple. Heureusement, nous pouvons le faire avec contravariance, c'est pourquoi il y a une surcharge pour Collections.sort d:

public static <T> void sort(List<T> list, Comparator<? super T> c)
29
répondu Jon Skeet 2012-03-28 07:27:37

Voici un extrait pertinent de Java Génériques et Collections:

2.4. Le principe Get and Put

Il peut être judicieux d'insérer des caractères génériques dans la mesure du possible, mais comment décidez-vous quel caractère générique utiliser? Où devriez-vous utiliser extends, où devriez-vous utiliser super, et où est-il inapproprié d'utiliser un caractère générique?

Heureusement, un principe simple détermine ce qui est approprié.

Le principe Get et Put : utiliser un extends générique lorsque vous obtenez seulement valeurs d'une structure, utilisez un super Joker lorsque vous mettez uniquement des valeurs dans une structure, et n'utilisez pas de caractère générique quand vous obtenez et mettez.

, Nous avons déjà vu ce principe à l'œuvre dans la signature de la méthode de copie:

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

La méthode obtient des valeurs de la source src, elle est donc déclarée avec un caractère générique extends , et il met des valeurs dans le DST de destination, donc il est déclaré avec un caractère générique super. Chaque fois que vous utilisez un itérateur, vous obtenez des valeurs d'une structure, donc utilisez un extends joker. Voici une méthode qui prend un ensemble de chiffres, convertit chaque double, et les résume:

public static double sum(Collection<? extends Number> nums) {
    double s = 0.0;
    for (Number num : nums) s += num.doubleValue();
    return s;
}
33
répondu Sean Patrick Floyd 2010-10-05 14:23:50

Par exemple, lors de l'implémentation des Collections .addAll () méthode, vous avez besoin d'une collection qui peut contenir un type T ou un supertype de T. la méthode ressemble alors à:

public static <T> void addAll(Collection<? super T> collection, T... objects) {
    // Do something
}
6
répondu Marc 2010-10-05 06:02:41