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 Duke
und 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 SweetCreator
Klasse 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 @Override
Anmerkung 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 Duke
ist 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 ArrayList
Klasse instanziieren, die die List
Schnittstelle 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 List
Typ 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 MetalGearCharacter
die giveOrderToTheArmy
Methode deklariert ist.
Das instanceof
reservierte 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 BigBoss
Beispiel, 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 super
reservierte 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 super
reservierte 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
@Override
Anmerkung 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.