We continue to translate a series of articles with puzzles on Java. The last post about the line caused a surprisingly heated discussion . We hope that you will not pass by this article either. And yes - we now invite the tenth anniversary stream of our Java Developer course.
According to the legendary Venkat Subramaniam, polymorphism is the most important concept in object-oriented programming. Polymorphism — or the ability of an object to perform specialized actions based on its type — is what makes Java code flexible. Design patterns, such as Command (Command), Observer, Decorator, Strategy, and many others created by a gang of four, all use some form of polymorphism. Mastering this concept will greatly improve your ability to think through software solutions.
You can get the source code for this article and experiment here: https://github.com/rafadelnero/javaworld-challengers
In this article, we will focus on the relationship between polymorphism and inheritance. The main thing to keep in mind is that polymorphism requires inheritance or interface implementation . You can see this in the example below with Duke and 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(); } }
The output of this code will be as follows:
Punch! Fly!
Since the concrete implementations are defined, the methods of Duke
and Juggy
will be called.
Is overloading of the method a polymorphism? Many programmers confuse a polymorphism relationship with overriding methods and overloading methods . In fact, only redefinition of the method is true polymorphism. Overload uses the same method name, but different parameters. Polymorphism is a broad term, so there will always be discussions on this topic.
The big advantage and purpose of using polymorphism is to reduce the client class relatedness with the implementation. Instead of hardcoding, the client class gets a dependency implementation to perform the necessary action. Thus, the client class knows a minimum to perform its actions, which is an example of weak binding.
To better understand the purpose of polymorphism, take a look at 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(); } }
In this example, you can see that the SweetCreator
class knows only about the SweetProducer
class. He does not know the realization of every Sweet
. This separation gives us the flexibility to update and reuse our classes, and this makes the code much easier to maintain. When designing a code, always look for ways to make it as flexible and convenient as possible. Polymorphism is a very powerful way to use for this purpose.
The @Override
obliges the programmer to use the same method signature that needs to be redefined. If the method is not overridden, there will be a compilation error.
You can change the return type of the overridden method if it is a covariant type . The covariant type is basically a subclass of the return value.
Consider an example:
public abstract class JavaMascot { abstract JavaMascot getMascot(); } public class Duke extends JavaMascot { @Override Duke getMascot() { return new Duke(); } }
Since Duke
is a JavaMascot
, we can change the type of the return value during the override.
We constantly use polymorphism in the base Java classes. One very simple example is to create an instance of the ArrayList
class with a type declaration as the List
interface.
List<String> list = new ArrayList<>();
Consider sample code using the Java Collections API without polymorphism:
public class ListActionWithoutPolymorphism { // void executeVectorActions(Vector<Object> vector) {/* */} void executeArrayListActions(ArrayList<Object> arrayList) {/* */} void executeLinkedListActions(LinkedList<Object> linkedList) {/* */} void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList<Object> copyOnWriteArrayList) { /* */} } public class ListActionInvokerWithoutPolymorphism { listAction.executeVectorActions(new Vector<>()); listAction.executeArrayListActions(new ArrayList<>()); listAction.executeLinkedListActions(new LinkedList<>()); listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList<>()); }
Disgusting code, isn't it? Imagine that you need to accompany him! Now consider the same example with polymorphism:
public static void main(String... polymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(); } public class ListAction { void executeListActions(List<Object> list) { // } } 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<>()); } }
The advantage of polymorphism is flexibility and extensibility. Instead of creating several different methods, we can declare one method that gets the type List
.
You can call specific methods in a polymorphic method call, this is due to flexibility. Here is an example:
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"); // // metalGearCharacter.giveOrderToTheArmy("Attack!"); if (metalGearCharacter instanceof BigBoss) { ((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!"); } } public static void main(String... specificPolymorphismInvocation) { executeActionWith(new SolidSnake()); executeActionWith(new BigBoss()); } }
The technique we use here is casting or consciously changing the type of an object at run time.
Note that calling a specific method is possible only when casting a more general type to a more specific type. A good analogy would be to tell the compiler explicitly: "Hey, I know what I'm doing here, so I'm going to cast the object to a specific type and will use this method."
Referring to the example above, the compiler has a good reason for not accepting calls to certain methods: the class that is passed must be SolidSnake
. In this case, the compiler has no way to ensure that each MetalGearCharacter
subclass has the giveOrderToTheArmy
method.
Notice the instanceof
reserved word. Before calling a particular method, we asked if MetalGearCharacter
instance (instance) of BigBoss
. If this is not a BigBoss
instance, we will get the following exception:
Exception in thread `main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake cannot be cast to com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss
super
keywordWhat if we want to refer to an attribute or method from the parent class? In this case, we can use the super
keyword.
For example:
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 the executeAction
method of the Duke
class calls the method of the parent class. Then we perform a specific action from the Duke
class. That is why we can see both messages in the output:
The Java Mascot is about to execute an action! Duke is going to punch!
Let's check what you learned about polymorphism and inheritance.
In this task you are given several methods from Matt Groening's The Simpsons, Vavam is required to unravel what the output for each class will be. First, carefully review the following code:
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 be the result? Do not use IDE to find out! The goal is to improve your code analysis skills, so try to solve it yourself.
Choose your answer (the correct answer can be found at the end of the article).
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
For the next method call:
new Lisa().talk("Sax :)");
The output will be "I love Sax!". This is because we are passing a string to a method, and the Lisa
class has such a method.
For the next call:
Simpson simpson = new Bart("D'oh"); simpson.talk();
The output will be "Eat my shorts!". This is because we initialize the Simpson
type with Bart
.
Now look, this is a bit more complicated:
Lisa lisa = new Lisa(); lisa.talk();
Here we use method overloading with inheritance. We don't pass anything to the talk
method, so the Simpson
talk
method is called.
In this case, the output will be "Simpson!".
Here is another one:
((Bart) simpson).prank();
In this case, the string prank
was transferred when creating an instance of the Bart
class via new Bart("D'oh");
. In this case, the super.prank()
method is first called, and then the part method prank()
from the Bart
class. The output will be:
"D'oh" "Knock Homer down"
A common mistake is to think that you can call a particular method without using a cast.
Another mistake is uncertainty about what method will be called upon polymorphic class instance creation. Remember that the method being called is a method of the created instance.
Also remember that overriding a method is not a method overload.
You cannot override a method if the parameters differ. You can change the return type of the overridden method if the return type is a subclass.
The created instance determines which method will be invoked using polymorphism.
The @Override
requires the programmer to use the overridden method; otherwise, a compiler error will occur.
Polymorphism can be used with ordinary classes, abstract classes and interfaces.
Most design patterns depend on some form of polymorphism.
The only way to call your desired method in a polymorphic subclass is to use a type conversion.
You can create a powerful code structure using polymorphism.
Experiment. Through this, you will be able to master this powerful concept!
The answer is D.
The output will be:
I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down
As always, I welcome your comments and questions. And we are waiting for Vitali in the open lesson .
Source: https://habr.com/ru/post/429120/
All Articles