Polymorphismus und Vererbung in Java

Nach der Legende Venkat Subramaniam ist Polymorphismus das wichtigste Konzept in der objektorientierten Programmierung. Polymorphismus - oder die Fähigkeit eines Objekts, spezielle Aktionen basierend auf seinem Typ auszuführen - macht Java-Code flexibel. Entwurfsmuster wie Command, Observer, Decorator, Strategy und viele andere, die von der Gang Of Four erstellt wurden, verwenden alle irgendeine Form von Polymorphismus. Die Beherrschung dieses Konzepts verbessert Ihre Fähigkeit, Lösungen für Programmierprobleme zu finden, erheblich.

Holen Sie sich den Code

Sie können den Quellcode für diese Herausforderung abrufen und Ihre eigenen Tests hier ausführen: //github.com/rafadelnero/javaworld-challengers

Schnittstellen und Vererbung im Polymorphismus

Mit diesem Java Challenger konzentrieren wir uns auf die Beziehung zwischen Polymorphismus und Vererbung. Die Hauptsache ist, dass Polymorphismus Vererbung oder Schnittstellenimplementierung erfordert . Sie können dies im folgenden Beispiel mit Duke und Juggy sehen:

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

Die Ausgabe dieses Codes lautet:

 Punch! Fly! 

Aufgrund ihrer spezifischen Implementierungen beider Dukeund Juggy‚s Aktionen werden ausgeführt.

Überlastet die Methode den Polymorphismus?

Viele Programmierer sind verwirrt über die Beziehung zwischen Polymorphismus und Methodenüberschreibung und Methodenüberladung. Tatsächlich ist nur das Überschreiben von Methoden ein echter Polymorphismus. Überladen hat denselben Methodennamen, die Parameter sind jedoch unterschiedlich. Polymorphismus ist ein weit gefasster Begriff, daher wird es immer Diskussionen zu diesem Thema geben.

Was ist der Zweck des Polymorphismus?

Der große Vorteil und Zweck der Verwendung von Polymorphismus besteht darin, die Clientklasse vom Implementierungscode zu entkoppeln. Anstatt fest codiert zu sein, erhält die Clientklasse die Implementierung, um die erforderliche Aktion auszuführen. Auf diese Weise weiß die Client-Klasse gerade genug, um ihre Aktionen auszuführen. Dies ist ein Beispiel für eine lose Kopplung.

Um den Zweck des Polymorphismus besser zu verstehen, werfen Sie einen Blick auf 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(); } } 

In diesem Beispiel sehen Sie, dass die SweetCreatorKlasse nur die  SweetProducer Klasse kennt . Es kennt nicht die Implementierung von jedem Sweet. Diese Trennung gibt uns Flexibilität bei der Aktualisierung und Wiederverwendung unserer Klassen und erleichtert die Wartung des Codes erheblich. Suchen Sie beim Entwerfen Ihres Codes immer nach Möglichkeiten, ihn so flexibel und wartbar wie möglich zu gestalten. Polymorphismus ist eine sehr leistungsfähige Technik für diese Zwecke.

Tipp : Die @OverrideAnmerkung verpflichtet den Programmierer, dieselbe Methodensignatur zu verwenden, die überschrieben werden muss. Wenn die Methode nicht überschrieben wird, liegt ein Kompilierungsfehler vor.

Kovariante Rückgabetypen beim Überschreiben von Methoden

Es ist möglich, den Rückgabetyp einer überschriebenen Methode zu ändern, wenn es sich um einen kovarianten Typ handelt. Ein kovarianter Typ ist im Grunde eine Unterklasse des Rückgabetyps. Betrachten Sie ein Beispiel:

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

Da dies ein Dukeist JavaMascot, können wir den Rückgabetyp beim Überschreiben ändern.

Polymorphismus mit den Java-Kernklassen

In den Java-Kernklassen verwenden wir ständig Polymorphismus. Ein sehr einfaches Beispiel ist, wenn wir die ArrayListKlasse instanziieren, die die ListSchnittstelle als Typ deklariert   :

 List list = new ArrayList(); 

Betrachten Sie dieses Codebeispiel mithilfe der Java Collections-API ohne Polymorphismus:

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

Hässlicher Code, nicht wahr? Stellen Sie sich vor, Sie versuchen es aufrechtzuerhalten! Schauen Sie sich nun dasselbe Beispiel mit Polymorphismus an:

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

Der Vorteil des Polymorphismus ist Flexibilität und Erweiterbarkeit. Anstatt mehrere verschiedene Methoden zu erstellen, können wir nur eine Methode deklarieren, die den generischen ListTyp empfängt .

Aufrufen bestimmter Methoden in einem polymorphen Methodenaufruf

Es ist möglich, bestimmte Methoden in einem polymorphen Aufruf aufzurufen, dies geht jedoch zu Lasten der Flexibilität. Hier ist ein Beispiel:

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

Die Technik, die wir hier verwenden, ist das Casting oder das absichtliche Ändern des Objekttyps zur Laufzeit.

Beachten Sie, dass eine bestimmte Methode nur aufgerufen werden kann , wenn der generische Typ in den bestimmten Typ umgewandelt wird. Eine gute Analogie wäre, dem Compiler explizit zu sagen: "Hey, ich weiß, was ich hier mache, also werde ich das Objekt in einen bestimmten Typ umwandeln und eine bestimmte Methode verwenden."  

In Bezug auf das obige Beispiel gibt es einen wichtigen Grund, warum der Compiler einen bestimmten Methodenaufruf nicht akzeptiert: Die Klasse, die übergeben wird, könnte sein SolidSnake. In diesem Fall kann der Compiler nicht sicherstellen, dass für jede Unterklasse von MetalGearCharacterdie giveOrderToTheArmyMethode deklariert ist.

Das instanceofreservierte Schlüsselwort

Achten Sie auf das reservierte Wort instanceof. Vor dem Aufrufen der spezifischen Methode haben wir gefragt, ob MetalGearCharacter" instanceof" ist BigBoss. Wenn es nicht ist ein BigBossBeispiel, würden wir die folgende Ausnahmemeldung erhalten:

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

Das superreservierte Schlüsselwort

Was wäre, wenn wir auf ein Attribut oder eine Methode aus einer Java-Superklasse verweisen wollten? In diesem Fall könnten wir das superreservierte Wort verwenden. Zum Beispiel:

 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.

Was ist über Polymorphismus zu beachten?

  • Die erstellte Instanz bestimmt, welche Methode bei Verwendung von Polymorphismus aufgerufen wird.
  • Die @OverrideAnmerkung verpflichtet den Programmierer, eine überschriebene Methode zu verwenden. Andernfalls tritt ein Compilerfehler auf.
  • Polymorphismus kann mit normalen Klassen, abstrakten Klassen und Schnittstellen verwendet werden.
  • Die meisten Entwurfsmuster hängen von irgendeiner Form von Polymorphismus ab.
  • Die einzige Möglichkeit, eine bestimmte Methode in Ihrer polymorphen Unterklasse zu verwenden, ist die Verwendung von Casting.
  • Mit Polymorphismus können Sie eine leistungsstarke Struktur in Ihrem Code entwerfen.
  • Führen Sie Ihre Tests aus. Auf diese Weise können Sie dieses leistungsstarke Konzept beherrschen!

Lösungsschlüssel

Die Antwort auf diese Java - Herausforderer ist D . Die Ausgabe wäre:

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

Diese Geschichte "Polymorphismus und Vererbung in Java" wurde ursprünglich von JavaWorld veröffentlicht.