Supposons que j'ai cette hiérarchie de classe ...
public abstract class Animal {
public abstract void eat();
public abstract void talk();
}
class Dog extends Animal {
@Override
public void eat() {
}
@Override
public void talk() {
}
}
class Cat extends Animal {
@Override
public void eat() {
}
@Override
public void talk() {
}
}
Et puis j'ai ...
public static <T extends Animal> void addAnimal(T animal) {
animal.eat();
animal.talk();
}
public static void addAnimalPoly(Animal animal) {
animal.eat();
animal.talk();
}
Quelle est la différence lors de l'utilisation de paramètres de type borné ou de polymorphisme?
Et quand utiliser l'un ou l'autre?
java
polymorphism
generics
HugoMelo
la source
la source
addAnimals(List<Animal>)
et d'ajouter une liste de chats!Réponses:
Ces deux exemples sont équivalents et, en fait, seront compilés dans le même bytecode.
Il y a deux façons d'ajouter un type générique borné à une méthode comme dans votre premier exemple fera n'importe quoi.
Passer le paramètre de type à un autre type
Ces deux signatures de méthode finissent par être les mêmes dans le code octet, mais le compilateur applique la sécurité des types:
public static <T extends Animal> void addAnimals(Collection<T> animals)
public static void addAnimals(Collection<Animal> animals)
Dans le premier cas, seul un
Collection
(ou sous-type) deAnimal
est autorisé. Dans le second cas, unCollection
(ou un sous-type) avec un type génériqueAnimal
ou un sous-type est autorisé.Par exemple, ce qui suit est autorisé dans la première méthode mais pas dans la seconde:
La raison en est que la seconde autorise uniquement les collections d'animaux, tandis que la première autorise les collections de tout objet attribuable à l'animal (c'est-à-dire les sous-types). Notez que si cette liste était une liste d'animaux qui contenaient un chat, l'une ou l'autre méthode l'accepterait: le problème est la spécification générique de la collection, pas ce qu'elle contient réellement.
Retour d'objets
L'autre fois que cela compte, c'est le retour des objets. Supposons que la méthode suivante existe:
Vous seriez en mesure de faire ce qui suit avec lui:
Bien qu'il s'agisse d'un exemple artificiel, il y a des cas où cela a du sens. Sans génériques, la méthode devrait revenir
Animal
et vous devrez ajouter un transtypage de type pour le faire fonctionner (ce que le compilateur ajoute au code d'octets de toute façon dans les coulisses).la source
Utilisez des génériques au lieu de downcasting. Le "downcasting" est mauvais, passant d'un type plus général à un type plus spécifique:
... vous faites confiance à
a
un chat, mais le compilateur ne peut pas le garantir. Il pourrait s'avérer être un chien lors de l'exécution.Voici où vous utiliseriez des génériques:
Vous pouvez maintenant spécifier que vous voulez un chasseur de chats:
Maintenant, le compilateur peut garantir que hunterC ne capturera que les chats, et hunterD ne capturera que les chiens.
Il vous suffit donc d'utiliser un polymorphisme régulier si vous souhaitez simplement gérer des classes spécifiques comme type de base. L'upcasting est une bonne chose. Mais si vous vous trouvez dans une situation où vous devez gérer des classes spécifiques comme leur propre type, génériquement, utilisez des génériques.
Ou, vraiment, si vous constatez que vous devez abattre, utilisez des génériques.
EDIT: le cas le plus général est lorsque vous voulez différer la décision des types de types à gérer. Ainsi, les types deviennent un paramètre, ainsi que les valeurs.
Disons que je veux que ma classe Zoo gère les chats ou les éponges. Je n'ai pas de super classe commune. Mais je peux toujours utiliser:
le degré de verrouillage dépend de ce que vous essayez de faire;)
la source
Cette question est ancienne, mais un facteur important à considérer semble avoir été laissé de côté quant au moment d'utiliser le polymorphisme par rapport aux paramètres de type borné. Ce facteur peut être légèrement tangent à l'exemple donné dans la question mais, je pense, très pertinent pour le plus général "Quand utiliser le polymorphisme vs les paramètres de type borné?"
TL; DR
Si vous vous retrouvez à déplacer du code d'une sous-classe vers une classe de base contre votre meilleur jugement, en raison de l'impossibilité d'y accéder de manière polymorphe, les paramètres de type bornés pourraient être une solution potentielle.
La réponse complète
Les paramètres de type délimités peuvent exposer des méthodes de sous-classe concrètes et non héritées pour une variable de membre héritée. Le polymorphisme ne peut pas
Pour élaborer en étendant votre exemple:
Si la classe abstraite AnimalOwner a été définie pour avoir un
protected Animal pet;
et opter pour le polymorphisme, le compilateur affichera une erreur sur lapet.scratchBelly();
ligne, vous indiquant que cette méthode n'est pas définie pour Animal.la source
Dans votre exemple, vous n'utilisez pas (et ne devriez pas) utiliser de type borné. N'utilisez des paramètres de type borné que lorsque vous le devez , car ils sont plus déroutants à comprendre.
Voici quelques situations où vous utiliserez des paramètres de type borné:
Paramètres des collections
alors vous pouvez appeler
zoo.add(dogs)
ne parviendrait pas à compiler sans<? extends Animal>
, car les génériques ne sont pas covariants.Sous-classement
pour limiter le type de sous-classe peut fournir.
Vous pouvez également utiliser plusieurs limites
<T extends A1 & A2 & A3>
pour vous assurer qu'un type est un sous-type de tous les types de la liste.la source