Dezign Patterns
Polymorphism
π¦ Definition: One interface, many forms...
π― Intent : The primary goal of polymorphism is to enable flexibility, reusability, and scalability in code by allowing one interface to work with different types of objects.
Types:
- Compile-time (method overloading)
- Runtime (method overriding)
Example 1:
class Animal {
void makeSound() {
System.out.println("Some sound");
}
}
class Cat extends Animal {
void makeSound() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
Animal a = new Cat(); // Polymorphism
a.makeSound(); // Outputs: Meow
}
}
Example 2:
public class AreaCalculator {
public double calculateArea(Shape shape) {
return shape.area();
}
}
based on the recived object
Shape <-- [Circle, Rectangle]
- β Static methods can't be overridden β they are hidden (method hiding).
- β Private methods can't be overridden β they are not inherited.
class Parent {
static void show() {
System.out.println("Static method in Parent");
}
}
class Child extends Parent {
static void show() {
System.out.println("Static method in Child");
}
}
Parent p = new Child();
p.show(); // calls Parent.show() because method is static
- No β constructors are not inherited, so they canβt be overridden.
- Thus, constructors do not support polymorphism.
- It's the process by which JVM decides which method to call at runtime when a parent class reference points to a child class object.
- JVM uses v-tables (virtual method tables) for method resolution.
- Each class has a table of methods.
- At runtime, JVM looks up the actual object's method in the v-table.
In method overloading, multiple methods in the same class share the same name, but differ by:
- Number of parameters
- Type of parameters
- Order of parameters
Note : compile time polymorphism doesn't depends on return type
int display(int a, int b, int c)
void display(String str)
Method overloading preference :
* Exact match > Widening > Boxing > Varargs
Can overloaded methods have different return types?
- β Yes, but only if the parameter list is different.
- Return type alone cannot differentiate an overloaded method.
int test(int a) { return a; }
// This will cause a compile error if:
String test(int a) { return "a"; } // β Invalid: only return type differs
Can you overload static methods?
- β Yes. Static methods can be overloaded β they follow the same rules as instance methods.
- β Yes. Constructors can be overloaded just like methods.
class Person {
Person() {}
Person(String name) {}
}
- β Yes, technically. But the JVM only calls the exact signature public static void main(String[] args).
public static void main(String[] args) {
main(5); // valid call
}
public static void main(int x) {
System.out.println("Overloaded main: " + x);
}
void test(int a, long b) {}
void test(long a, int b) {}
test(10, 20); // β ambiguous β could match either
// can be solved by calling
// test(10l, 20)
// test(10,20l)
- β Yes. But varargs is the last option in method resolution.
void print(String... names) {}
void print(String name) {}
print("John"); // calls non-varargs version
void print(int a) {}
void print(Integer a) {}
print(5); // picks `int`, but could be confusing
void test(long x) {
System.out.println("long");
}
void test(Integer x) {
System.out.println("Integer");
}
test(10); // Output: long // int is widened to long
void test(byte x) {
System.out.println("byte");
}
void test(short x) {
System.out.println("short");
}
test((byte) 5); // byte
test((short) 5); // short
test(5); // will fail to compile if no int version
// by default number is treated as int
Method overloading preference :
* Exact match > Widening > Boxing > Varargs
class OverloadExample {
void test(int x) {
System.out.println("int");
}
void test(long x) {
System.out.println("long");
}
void test(float x) {
System.out.println("float");
}
void test(double x) {
System.out.println("double");
}
}
OverloadExample obj = new OverloadExample();
obj.test(10); // int
obj.test(10L); // long
obj.test(10.5f); // float
obj.test(10.5); // double
void test(float f) {
System.out.println("float");
}
void test(double d) {
System.out.println("double");
}
test(10); // int -> float widening // output : float
void test(long l) {
System.out.println("long");
}
void test(Integer i) {
System.out.println("Integer");
}
test(10); // widening preferred over boxing output : long
void test(Object obj) {
System.out.println("Object");
}
void test(String str) {
System.out.println("String");
}
test(null); // more specific over load method is chosen // output : String
void test(long l) {
System.out.println("long");
}
void test(int... i) {
System.out.println("int varargs");
}
test(10); // Widening beats varargs // output : long