Java Polymorphism Examples and Lab
Java Polymorphism Examples and Lab
Method overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass. This enhances polymorphism as it enables the use of a superclass type reference to dynamically invoke the overridden methods of various subclasses at runtime. For example, in the 'Person' and 'Father' classes, the method 'role()' is overridden in the 'Father' class, and when called on a 'Person' reference holding a 'Father' object, it executes the 'Father's implementation. Similarly, in the 'Animal', 'Pig', and 'Dog' classes, the 'animalSound()' method is overridden in each subclass to provide specific sounds. This same method name invokes different implementations based on the subclass instance .
Method signatures, which consist of the method name and its parameter list, are essential in distinguishing between method overloading and method overriding. In method overloading, different methods within the same class share the same name but have different parameter lists, as seen in the 'Helper' class where 'Multiply' methods differ by argument types (integers and doubles). In method overriding, the child class provides a specific implementation for a method already defined in its parent class with the same signature, as seen in the 'Animal' class hierarchy where 'animalSound()' methods in 'Pig' and 'Dog' have the same signature as in the 'Animal' class but provide different functionalities .
The 'Geeks' class illustrates dynamic method dispatch through the use of a superclass reference ('Parent') pointing to subclass objects ('subclass1' and 'subclass2'). When 'Print()' is called on these references, it dynamically binds to the appropriate method version at runtime, executing 'subclass1's and 'subclass2's respective 'Print()' implementations, showing the different outputs—'subclass1' and 'subclass2'. This demonstrates dynamic method dispatch, a fundamental aspect of runtime polymorphism, where method resolution is delayed until runtime .
Method overriding impacts software design patterns by promoting polymorphic behavior, which is fundamental to many patterns such as the Strategy and Observer patterns. In the examples, method overriding in classes like 'Father', 'subclass1', and 'subclass2', alongside 'Pig' and 'Dog', enables flexibility in substituting algorithms and responses, supporting design patterns that require interchangeable behaviors. This capability facilitates encapsulating algorithms and varying parts of the system without modifying client code, aligning with the open/closed principle and enhancing design scalability and adaptability .
In the 'Main' class, polymorphism is demonstrated by declaring a reference of type 'Person', which is the superclass, and assigning it an object of type 'Father', the subclass. When the 'role()' method is called on this reference, the overridden method in the 'Father' class is executed, displaying 'I am a father.' This behavior illustrates polymorphism, where the call to the method 'role()' on a superclass reference holding a subclass object results in the execution of the subclass's method implementation .
Method overriding in 'Pig' and 'Dog' enhances system flexibility and extensibility by allowing new classes to define specific behaviors while adhering to a general contract. Each subclass provides its unique implementation for 'animalSound()' without altering the original 'Animal' class, facilitating future extensions like adding more subclasses with distinct sounds. This design leverages polymorphism, enabling code reuse and robust modification with minimal impact on existing functionalities, thus accommodating growth and changes efficiently .
Specifying parameter types in method overloading is crucial as it determines which version of the method is called during a function call. In the 'Helper' class, the 'Multiply' method is overloaded to accept both integer and double parameters. The choice of method executed depends on the argument types supplied during the call (e.g., passing integers calls the integer version, and passing doubles calls the double version). This specificity allows for more flexible and intuitive code usage, leveraging Java's compile-time type checking to determine method execution paths .
In the provided examples, inheritance allows subclasses to reuse code from superclasses, promoting maintainability by centralizing common behaviors. For instance, the 'Father' class inherits from 'Person', and both 'subclass1' and 'subclass2' inherit from 'Parent', each inheriting core functionalities (e.g., method headers) and only implementing specific differences. This reuse reduces redundancy and ensures consistency, while any superclass changes automatically propagate to subclasses, simplifying maintenance without modifying each subclass individually .
Method overloading occurs when multiple methods have the same name but different parameter lists within the same class. It is demonstrated in the 'Helper' class where 'Multiply' methods accept different types of parameters (integer and double) but retain the same method name. On the other hand, method overriding involves redefining a method in a subclass that already exists in its superclass. The overriden methods have the same signatures as in the superclass, but different implementations. This is demonstrated in subclasses 'subclass1' and 'subclass2' where they both override the 'Print' method from the 'Parent' class, providing their unique behaviors .
The 'Animal' class hierarchy exemplifies object-oriented principles, particularly inheritance and polymorphism, through method overriding. The 'Animal' class defines the method 'animalSound()' with a general implementation. Its subclasses, 'Pig' and 'Dog', override this method to provide specific behaviors ('wee wee' and 'bow wow', respectively). This use of method overriding allows each subclass to customize inherited behaviors while maintaining the same method signature, supporting polymorphism where the reference type determines the method execution at runtime .