Java Challengers # 3: Polymorphisme et héritage
Nous continuons de traduire une série d'articles avec des problèmes Java. Le dernier message sur les lignes a provoqué une discussion étonnamment passionnée . Nous espérons que vous ne passerez pas non plus par cet article. Et oui - nous vous invitons maintenant au dixième anniversaire de notre cours de développeur Java .
Selon le légendaire Venkat Subramaniam, le polymorphisme est le concept le plus important dans la programmation orientée objet. Le polymorphisme - ou la capacité d'un objet à effectuer des actions spécialisées en fonction de son type - est ce qui rend le code Java flexible. Les modèles de conception tels que la commande, l'observateur, le décorateur, la stratégie et bien d'autres créés par le gang des quatre utilisent tous une forme de polymorphisme. La maîtrise de ce concept améliorera considérablement votre capacité à réfléchir aux solutions logicielles.

Vous pouvez prendre le code source de cet article et expérimenter ici: https://github.com/rafadelnero/javaworld-challengers
Interfaces et héritage dans le polymorphisme
Dans cet article, nous nous concentrerons sur la relation entre le polymorphisme et l'héritage. La principale chose à garder à l'esprit est que le polymorphisme nécessite l' héritage ou la mise en œuvre d'une interface . Vous pouvez le voir dans l'exemple ci-dessous avec Duke et Juggy
:
public abstract class JavaMascot { public abstract void executeAction(); } public class Duke extends JavaMascot { @Override public void executeAction() { System.out.println("Punch!"); } } public class Juggy extends JavaMascot { @Override public void executeAction() { System.out.println("Fly!"); } } public class JavaMascotTest { public static void main(String... args) { JavaMascot dukeMascot = new Duke(); JavaMascot juggyMascot = new Juggy(); dukeMascot.executeAction(); juggyMascot.executeAction(); } }
La sortie de ce code sera comme ceci:
Punch! Fly!
Comme des implémentations spécifiques sont définies, les méthodes Duke
et Juggy
seront appelées.
La surcharge de méthode est-elle un polymorphisme? De nombreux programmeurs confondent la relation du polymorphisme avec la surcharge et la surcharge . En fait, seule la redéfinition de la méthode est un vrai polymorphisme. La surcharge utilise le même nom de méthode, mais des paramètres différents. Le polymorphisme est un terme large, il y aura donc toujours des discussions sur ce sujet.
Quel est le but du polymorphisme
Un grand avantage et objectif de l'utilisation du polymorphisme est de réduire la connexion de classe client avec l'implémentation. Au lieu de hardcode, la classe client obtient une implémentation de la dépendance pour effectuer l'action nécessaire. Ainsi, la classe client connaît le minimum pour terminer ses actions, ce qui est un exemple de liaison faible.
Pour mieux comprendre l'objectif du polymorphisme, jetez un œil à SweetCreator
:
public abstract class SweetProducer { public abstract void produceSweet(); } public class CakeProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Cake produced"); } } public class ChocolateProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Chocolate produced"); } } public class CookieProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Cookie produced"); } } public class SweetCreator { private List<SweetProducer> sweetProducer; public SweetCreator(List<SweetProducer> sweetProducer) { this.sweetProducer = sweetProducer; } public void createSweets() { sweetProducer.forEach(sweet -> sweet.produceSweet()); } } public class SweetCreatorTest { public static void main(String... args) { SweetCreator sweetCreator = new SweetCreator(Arrays.asList( new CakeProducer(), new ChocolateProducer(), new CookieProducer())); sweetCreator.createSweets(); } }
Dans cet exemple, vous pouvez voir que la classe SweetCreator
connaît SweetCreator
la classe SweetProducer
. Il ne connaît pas la mise en œuvre de chaque Sweet
. Cette séparation nous donne la flexibilité de mettre à jour et de réutiliser nos classes, ce qui rend le code beaucoup plus facile à maintenir. Lors de la conception du code, recherchez toujours des moyens de le rendre aussi flexible et pratique que possible. Le polymorphisme est un moyen très puissant d'être utilisé à cette fin.
L' @Override
nécessite que le programmeur utilise la même signature de méthode, qui doit être remplacée. Si la méthode n'est pas remplacée, il y aura une erreur de compilation.
Types de retour covariants lors de la substitution d'une méthode
Vous pouvez modifier le type de retour d'une méthode substituée s'il s'agit d'un type covariant . Le type covariant est essentiellement une sous-classe de la valeur de retour.
Prenons un exemple:
public abstract class JavaMascot { abstract JavaMascot getMascot(); } public class Duke extends JavaMascot { @Override Duke getMascot() { return new Duke(); } }
Duke
étant JavaMascot
, nous pouvons changer le type de la valeur de retour lors de la substitution.
Polymorphisme dans les classes de base Java
Nous utilisons constamment le polymorphisme dans les classes Java de base. Un exemple très simple consiste à instancier une classe ArrayList
avec une déclaration de type en tant qu'interface List
.
List<String> list = new ArrayList<>();
Prenons un exemple de code qui utilise l'API Java Collections sans polymorphisme:
public class ListActionWithoutPolymorphism {
Code dégoûtant, non? Imaginez que vous devez l'accompagner! Considérons maintenant le même exemple avec le polymorphisme:
public static void main(String... polymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(); } public class ListAction { void executeListActions(List<Object> list) {
L'avantage du polymorphisme est la flexibilité et l'extensibilité. Au lieu de créer plusieurs méthodes différentes, nous pouvons déclarer une méthode qui obtient un type List
.
Appel de méthodes spécifiques pour une méthode polymorphe
Vous pouvez appeler des méthodes spécifiques avec un appel de méthode polymorphe, cela est dû à la flexibilité. Voici un exemple:
public abstract class MetalGearCharacter { abstract void useWeapon(String weapon); } public class BigBoss extends MetalGearCharacter { @Override void useWeapon(String weapon) { System.out.println("Big Boss is using a " + weapon); } void giveOrderToTheArmy(String orderMessage) { System.out.println(orderMessage); } } public class SolidSnake extends MetalGearCharacter { void useWeapon(String weapon) { System.out.println("Solid Snake is using a " + weapon); } } public class UseSpecificMethod { public static void executeActionWith(MetalGearCharacter metalGearCharacter) { metalGearCharacter.useWeapon("SOCOM");
La technique que nous utilisons ici consiste à lancer ou à modifier consciemment le type d'un objet au moment de l'exécution.
Veuillez noter qu'un appel à une méthode spécifique n'est possible que lors de la conversion d'un type plus général en un type plus spécifique. Une bonne analogie serait de dire explicitement au compilateur: "Hé, je sais ce que je fais ici, donc je vais convertir l'objet en un type spécifique et j'utiliserai cette méthode."
En se référant à l'exemple ci-dessus, le compilateur a une bonne raison de ne pas accepter l'appel de certaines méthodes: la classe transmise doit être SolidSnake
. Dans ce cas, le compilateur n'a aucun moyen de garantir que chaque sous-classe de MetalGearCharacter
possède une méthode giveOrderToTheArmy
.
Instance du mot clé
Notez le mot réservé instanceof
. Avant d'appeler une méthode spécifique, nous avons demandé si MetalGearCharacter
instance de BigBoss
. Si ce n'est pas une instance de BigBoss
, nous obtiendrons l'exception suivante:
Exception in thread `main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake cannot be cast to com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss
Mot super
clé super
Et si nous voulons faire référence à un attribut ou une méthode de la classe parente? Dans ce cas, nous pouvons utiliser le super
mot super
clé.
Par exemple:
public class JavaMascot { void executeAction() { System.out.println("The Java Mascot is about to execute an action!"); } } public class Duke extends JavaMascot { @Override void executeAction() { super.executeAction(); System.out.println("Duke is going to punch!"); } public static void main(String... superReservedWord) { new Duke().executeAction(); } }
L'utilisation du mot réservé super
dans la méthode executeAction
de la classe Duke
appelle la méthode de la classe parente. Ensuite, nous effectuons une action spécifique de la classe Duke
. C'est pourquoi nous pouvons voir les deux messages dans la sortie:
The Java Mascot is about to execute an action! Duke is going to punch!
Résoudre le problème du polymorphisme
Vérifions ce que vous avez appris sur le polymorphisme et l'héritage.
Dans cette tâche, vous disposez de plusieurs méthodes de The Simpsons de Matt Groening, des Wavs, vous devez déterminer ce que sera la sortie pour chaque classe. Tout d'abord, analysez soigneusement le code suivant:
public class PolymorphismChallenge { static abstract class Simpson { void talk() { System.out.println("Simpson!"); } protected void prank(String prank) { System.out.println(prank); } } static class Bart extends Simpson { String prank; Bart(String prank) { this.prank = prank; } protected void talk() { System.out.println("Eat my shorts!"); } protected void prank() { super.prank(prank); System.out.println("Knock Homer down"); } } static class Lisa extends Simpson { void talk(String toMe) { System.out.println("I love Sax!"); } } public static void main(String... doYourBest) { new Lisa().talk("Sax :)"); Simpson simpson = new Bart("D'oh"); simpson.talk(); Lisa lisa = new Lisa(); lisa.talk(); ((Bart) simpson).prank(); } }
Qu'en penses-tu? Quel sera le résultat? N'utilisez pas l'IDE pour le savoir! L'objectif est d'améliorer vos compétences en analyse de code, alors essayez de décider par vous-même.
Choisissez votre réponse (vous trouverez la bonne réponse à la fin de l'article).
A)
J'adore Sax!
D'oh
Simpson!
D'oh
B)
Sax :)
Mange mon short!
J'adore Sax!
D'oh
Renverser le circuit
C)
Sax :)
D'oh
Simpson!
Renverser le circuit
D)
J'adore Sax!
Mange mon short!
Simpson!
D'oh
Renverser le circuit
Qu'est-il arrivé? Comprendre le polymorphisme
Pour la méthode suivante, appelez:
new Lisa().talk("Sax :)");
la sortie sera "I love Sax!". En effet, nous passons la chaîne à la méthode et la classe Lisa
a une telle méthode.
Pour le prochain appel:
Simpson simpson = new Bart("D'oh"); simpson.talk();
La sortie sera "Mange mon short!". C'est parce que nous initialisons le type Simpson
avec Bart
.
Maintenant, regardez, c'est un peu plus délicat:
Lisa lisa = new Lisa(); lisa.talk();
Ici, nous utilisons la surcharge de méthode avec héritage. Nous ne transmettons rien à la méthode talk
, donc la méthode talk
de Simpson
appelée.
Dans ce cas, la sortie sera "Simpson!".
En voici un autre:
((Bart) simpson).prank();
Dans ce cas, la chaîne de prank
été passée lors de l'instanciation de la classe Bart
via un new Bart("D'oh");
. Dans ce cas, la méthode super.prank()
est appelée en premier, puis la méthode prank()
de la classe Bart
est appelée. La conclusion sera:
"D'oh" "Knock Homer down"
Erreurs de polymorphisme courantes
Une erreur courante consiste à penser que vous pouvez appeler une méthode spécifique sans utiliser de transtypages de type.
Une autre erreur est l'incertitude quant à la méthode qui sera appelée lors de l'instanciation polymorphe de la classe. N'oubliez pas que la méthode appelée est la méthode de l'instance instanciée.
Souvenez-vous également que remplacer une méthode n'est pas une surcharge d'une méthode.
Impossible de remplacer la méthode si les paramètres sont différents. Vous pouvez modifier le type de retour d'une méthode substituée si le type de retour est une sous-classe.
Ce que vous devez retenir du polymorphisme
L'instance créée détermine la méthode qui sera appelée lors de l'utilisation du polymorphisme.
L' @Override
nécessite que le programmeur utilise une méthode remplacée; sinon, une erreur de compilation se produira.
Le polymorphisme peut être utilisé avec des classes régulières, des classes abstraites et des interfaces.
La plupart des modèles de conception dépendent d'une certaine forme de polymorphisme.
La seule façon d'appeler votre méthode dans une sous-classe polymorphe est d'utiliser le transtypage de type.
Vous pouvez créer une structure de code puissante en utilisant le polymorphisme.
Expérience. Grâce à cela, vous pourrez maîtriser ce concept puissant!
La réponse
La réponse est D.
La conclusion sera:
I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down
Comme toujours, j'accueille vos commentaires et vos questions. Et nous attendons Vitaly dans une leçon ouverte .