Si vous voulez accéder à l'interface étendue d'un objet
MoreUseful, vous pouvez essayer un downcast. Si c'est le type correct, cela
fonctionnera. Autrement, vous allez recevoir une ClassCastException. Vous n'avez
pas besoin d'écrire un code spécial pour cette exception, car elle indique une erreur du
programmeur qui pourrait arriver n'importe où dans un programme.
La RTTI est plus riche qu'un simple cast. Par exemple, il y a une façon de
connaître le type que vous manipulez avant d'essayer de le downcaster. Tout le Chapitre 12
est consacré à l'étude de différents aspects du « run-time type identification »
Java.
Résumé
Polymorphisme signifie « différentes formes. » Dans la
programmation orientée objet, vous avez la même physionomie (l'interface commune dans la classe de
base) et différentes formes qui utilisent cette physionomie: les différentes versions des méthodes
dynamiquement attachées.
Vous avez vu dans ce chapitre qu'il est impossible de comprendre, ou même
créer, un exemple de polymorphisme sans utiliser l'abstraction et l'héritage. Le polymorphisme est
une notion qui ne peut pas être présenté séparément (comme on peut le faire par exemple avec un
switch), mais qui fonctionne plutôt en conjonction avec le schéma global #big
picture# des relation entre classes. Les gens sont souvent troublés par d'autres dispositifs
non-orientés objet de Java, comme la surcharge de méthode, qui sont parfois présentés comme étant
orientés objet. Ne soyez pas dupe: si ce n'est pas de la liaison tardive, ce n'est pas du
polymorphisme.
Pour utiliser le polymorphisme, et par conséquent les techniques orientées
objet, pertinemment dans vos programmes vous devez élargir votre vision de la programmation pour y
inclure non seulement les membres et les messages d'une classe individuelle, mais également ce qui
est partagé entre les classes et leurs rapports entre elles. Bien que ceci exige un effort
significatif, ça vaut vraiment le coup car il en résulte un développement plus rapide, un code
mieux organisé, des programmes extensibles et une maintenance plus facile.
Exercices
- Ajouter une nouvelle méthode à la classe de base de
Shapes.java qui affiche un message, mais sans la redéfinir dans les classes
dérivées. Expliquer ce qui se passe. Maintenant la redéfinir dans une des classes dérivées mais pas
dans les autres, et voir ce qui se passe. Finalement, la redéfinir dans toutes les classes
dérivées.
- Ajouter un nouveau type de Shape à
Shapes.java et vérifier dans main() que le polymorphisme
fonctionne pour votre nouveau type comme il le fait pour les anciens types.
- Changer Music3.java pour que what()
devienne une méthode toString() de la classe racine Object .
Essayer d'afficher les objets Instrument en utilisant
System.out.println() (sans aucun cast).
- Ajouter un nouveau type d'Instrument à
Music3.java et vérifier que le polymorphisme fonctionne pour votre nouveau
type.
- Modifier Music3.java pour qu'il crée de manière aléatoire
des objets Instrument de la même façon que Shapes.java le
fait.
- Créer une hiérarchie d'héritage de Rongeur:
Souris, Gerbille, Hamster, etc. Dans la classe
de base, fournir des méthodes qui sont communes à tous les Rongeurs, et les
redéfinir dans les classes dérivées pour exécuter des comportements différents dépendant du type
spécifique du Rongeur. Créer un tableau de Rongeur, le remplir
avec différent types spécifiques de Rongeurs, et appeler vos méthodes de la classe
de base pour voir ce qui arrive.
- Modifier l'Exercice 6 pour que Rongeur soit une classe
abstract. Rendre les méthodes de Rongeur abstraites dès que
possible.
- Créer une classe comme étant abstract sans inclure aucune
méthode abstract, et vérifier que vous ne pouvez créer aucune instance de cette
classe.
- Ajouter la classe Pickle à
Sandwich.java.
- Modifier l'Exercice 6 afin qu'il démontre l'ordre des initialisations des
classes de base et des classes dérivées. Maintenant ajouter des objets membres à la fois aux
classes de base et dérivées, et montrer dans quel ordre leurs initialisations se produisent durant
la construction.
- Créer une hiérarchie d'héritage à 3 niveaux. Chaque classe dans la
hiérarchie devra avoir une méthode finalize(), et devra invoquer correctement la
version de la classe de base de finalize(). Démontrer que votre hiérarchie
fonctionne de manière appropriée.
- Créer une classe de base avec deux méthodes. Dans la première méthode,
appeler la seconde méthode. Faire hériter une classe et redéfinir la seconde méthode. Créer un
objet de la classe dérivée, upcaster le vers le type de base, et appeler la première méthode.
Expliquer ce qui se passe.
- Créer une classe de base avec une méthode abstract
print() qui est redéfinie dans une classe dérivée. La version redéfinie de la
méthode affiche la valeur d'une variable int définie dans la classe dérivée. Au
point de définition de cette variable, lui donner une valeur non nulle. Dans le constructeur de la
classe de base, appeler cette méthode. Dans main(), créer un objet du type dérivé,
et ensuite appeler sa méthode print(). Expliquer les résultats.
- Suivant l'exemple de Transmogrify.java, créer une classe
Starship contenant une référence AlertStatus qui peut indiquer
trois états différents. Inclure des méthodes pour changer les états.
- Créer une classe abstract sans méthodes. Dériver une
classe et ajouter une méthode. Créer une méthode static qui prend une référence
vers la classe de base, effectue un downcast vers la classe dérivée, et appelle la méthode. Dans
main(), démontrer que cela fonctionne. Maintenant mettre la déclaration
abstract pour la méthode dans la classe de base, éliminant ainsi le besoin du
downcast.
[37] Pour les programmeurs C++,
ceci est analogue aux fonctions virtuelles pures du C++.