Polymorphisme et héritage en Java

Selon la légende Venkat Subramaniam, le polymorphisme est le concept le plus important de la programmation orientée objet. Le polymorphisme - ou la capacité d'un objet à exécuter 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 Command, Observer, Decorator, Strategy et bien d'autres créés par le Gang Of Four utilisent tous une forme de polymorphisme. La maîtrise de ce concept améliore considérablement votre capacité à réfléchir à des solutions aux défis de programmation.

Obtenez le code

Vous pouvez obtenir le code source de ce défi et exécuter vos propres tests ici: //github.com/rafadelnero/javaworld-challengers

Interfaces et héritage dans le polymorphisme

Avec ce Java Challenger, nous nous concentrons sur la relation entre le polymorphisme et l'héritage. La chose principale à garder à l'esprit est que le polymorphisme nécessite un héritage ou une implémentation d'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:

 Punch! Fly! 

En raison de leur mise en œuvre spécifiques, à la fois Dukeet Juggyles actions « seront exécutées.

La méthode surcharge-elle le polymorphisme?

De nombreux programmeurs sont confus quant à la relation entre le polymorphisme et le remplacement de méthode et la surcharge de méthode. En fait, seul le remplacement de méthode est un vrai polymorphisme. La surcharge partage le même nom de méthode mais les paramètres sont différents. Le polymorphisme est un terme large, il y aura donc toujours des discussions sur ce sujet.

Quel est le but du polymorphisme?

Le grand avantage et le but de l'utilisation du polymorphisme est de découpler la classe cliente du code d'implémentation. Au lieu d'être codée en dur, la classe client reçoit l'implémentation pour exécuter l'action nécessaire. De cette façon, la classe client en sait juste assez pour exécuter ses actions, ce qui est un exemple de couplage lâche.

Pour mieux comprendre le but 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; public SweetCreator(List 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 SweetCreatorclasse ne connaît que la  SweetProducer classe. Il ne connaît pas la mise en œuvre de chacun Sweet. Cette séparation nous donne la flexibilité de mettre à jour et de réutiliser nos classes, et cela rend le code beaucoup plus facile à maintenir. Lors de la conception de votre code, recherchez toujours des moyens de le rendre aussi flexible et maintenable que possible. le polymorphisme est une technique très puissante à utiliser à ces fins.

Astuce : L' @Overrideannotation oblige le programmeur à utiliser 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 dans le remplacement de méthode

Il est possible de modifier le type de retour d'une méthode remplacée s'il s'agit d'un type covariant. Un type covariant est essentiellement une sous-classe du type de retour. Prenons un exemple:

 public abstract class JavaMascot { abstract JavaMascot getMascot(); } public class Duke extends JavaMascot { @Override Duke getMascot() { return new Duke(); } } 

Parce que Dukec'est a JavaMascot, nous pouvons changer le type de retour lors de la substitution.

Polymorphisme avec les classes Java de base

Nous utilisons le polymorphisme tout le temps dans les classes Java de base. Un exemple très simple est lorsque nous instancions la ArrayListclasse déclarant l'   Listinterface comme un type:

 List list = new ArrayList(); 

Pour aller plus loin, considérez cet exemple de code utilisant l'API Java Collections sans polymorphisme:

 public class ListActionWithoutPolymorphism { // Example without polymorphism void executeVectorActions(Vector vector) {/* Code repetition here*/} void executeArrayListActions(ArrayList arrayList) {/*Code repetition here*/} void executeLinkedListActions(LinkedList linkedList) {/* Code repetition here*/} void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList copyOnWriteArrayList) { /* Code repetition here*/} } public class ListActionInvokerWithoutPolymorphism { listAction.executeVectorActions(new Vector()); listAction.executeArrayListActions(new ArrayList()); listAction.executeLinkedListActions(new LinkedList()); listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList()); } 

Code laid, n'est-ce pas? Imaginez essayer de le maintenir! Regardez 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 list) { // Execute actions with different lists } } public class ListActionInvoker { public static void main(String... masterPolymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(new Vector()); listAction.executeListActions(new ArrayList()); listAction.executeListActions(new LinkedList()); listAction.executeListActions(new CopyOnWriteArrayList()); } } 

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 seule méthode qui reçoit le Listtype générique .

Appel de méthodes spécifiques dans un appel de méthode polymorphe

Il est possible d'invoquer des méthodes spécifiques dans un appel polymorphe, mais cela se fait au détriment de 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"); // The below line wouldn't work // metalGearCharacter.giveOrderToTheArmy("Attack!"); if (metalGearCharacter instanceof BigBoss) { ((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!"); } } public static void main(String... specificPolymorphismInvocation) { executeActionWith(new SolidSnake()); executeActionWith(new BigBoss()); } } 

La technique que nous utilisons ici consiste à convertir ou à modifier délibérément le type d'objet au moment de l'exécution.

Notez qu'il est possible d'appeler une méthode spécifique uniquement lors du cast du type générique vers le type 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 utiliser une méthode spécifique."  

En se référant à l'exemple ci-dessus, il y a une raison importante pour laquelle le compilateur refuse d'accepter l'invocation de méthode spécifique: la classe qui est passée pourrait être SolidSnake. Dans ce cas, il n'y a aucun moyen pour le compilateur de s'assurer que chaque sous-classe de MetalGearCharactera la giveOrderToTheArmyméthode déclarée.

Le instanceofmot-clé réservé

Faites attention au mot réservé instanceof. Avant d'appeler la méthode spécifique, nous avons demandé si MetalGearCharacterest « instanceof» BigBoss. S'il ne s'agissait pas d' une BigBossinstance, nous recevrions le message d'exception suivant:

 Exception in thread "main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake cannot be cast to com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss 

Le supermot-clé réservé

Et si nous voulions référencer un attribut ou une méthode à partir d'une superclasse Java? Dans ce cas, nous pourrions utiliser le supermot réservé. 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(); } } 

Using the reserved word super in Duke’s executeAction method  invokes the superclass method.  We then execute the specific action from Duke. That’s why we can see both messages in the output below:

 The Java Mascot is about to execute an action! Duke is going to punch! 

Take the polymorphism challenge!

Let’s try out what you’ve learned about polymorphism and inheritance. In this challenge, you’re given a handful of methods from Matt Groening’s The Simpsons, and your challenge is to deduce what the output for each class will be. To start, analyze the following code carefully:

 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(); } } 

What do you think? What will the final output be? Don’t use an IDE to figure this out! The point is to improve your code analysis skills, so try to determine the output for yourself.

Choose your answer and you’ll be able to find the correct answer below.

 A) I love Sax! D'oh Simpson! D'oh B) Sax :) Eat my shorts! I love Sax! D'oh Knock Homer down C) Sax :) D'oh Simpson! Knock Homer down D) I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down 

What just happened? Understanding polymorphism

For the following method invocation:

 new Lisa().talk("Sax :)"); 

the output will be “I love Sax!” This is  because we are passing a String to the method and Lisa has the method.

For the next invocation:

 Simpson simpson = new Bart("D'oh");

simpson.talk();

The output will be "Eat my shorts!" This is because we’re instantiating  the Simpson type with Bart.

Now check this one, which is a little trickier:

 Lisa lisa = new Lisa(); lisa.talk(); 

Here, we are using method overloading with inheritance. We are not passing anything to the talk method, which is why the Simpson talk method is invoked.  In this case the output will be:

 "Simpson!" 

Here’s one more:

 ((Bart) simpson).prank(); 

In this case, the prank String was passed when we instantiated the Bart class with new Bart("D'oh");. In this case,  first the super.prank method will be invoked, followed by the specific prank method from Bart. The output will be:

 "D'oh" "Knock Homer down" 

Video challenge! Debugging Java polymorphism and inheritance

Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video you can follow along while I debug and explain the Java polymorphism challenge:

Common mistakes with polymorphism

It’s a common mistake to think it’s possible to invoke a specific method without using casting.

Another mistake is being unsure what method will be invoked when instantiating a class polymorphically. Remember that the method to be invoked is the method of the created instance.

Also remember that method overriding is not method overloading.

It’s impossible to override a method if the parameters are different. It is possible to change the return type of the overridden method if the return type is a subclass of the superclass method.

Ce qu'il faut retenir du polymorphisme

  • L'instance créée déterminera quelle méthode sera appelée lors de l'utilisation du polymorphisme.
  • L' @Overrideannotation oblige le programmeur à utiliser une méthode remplacée; sinon, il y aura une erreur du compilateur.
  • Le polymorphisme peut être utilisé avec des classes normales, des classes abstraites et des interfaces.
  • La plupart des modèles de conception dépendent d'une forme de polymorphisme.
  • La seule façon d'utiliser une méthode spécifique dans votre sous-classe polymorphe est d'utiliser la conversion.
  • Il est possible de concevoir une structure puissante dans votre code en utilisant le polymorphisme.
  • Exécutez vos tests. En faisant cela, vous pourrez maîtriser ce concept puissant!

Clé de réponse

La réponse à ce challenger Java est D . Le résultat serait:

 I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down 

Cette histoire, "Polymorphisme et héritage en Java" a été publiée à l'origine par JavaWorld.