β˜• Java Complete Guide

Java Polymorphism

Static aur Dynamic β€” dono types ka complete deep guide with programs aur explanations

πŸ”΅ Static = Compile Time πŸ”΄ Dynamic = Runtime

🧠 Polymorphism Kya Hai?

"Poly = Many, Morph = Forms" β€” ek cheez ke bahut saare roop!

Real Life mein ek TEACHER β€” class mein Teacher, ghar pe Father/Mother, ground pe Coach. Same person, alag alag roles!
Java mein β€” ek METHOD NAAM β†’ alag alag kaam. add(int,int) numbers add karo, add(String,String) strings join karo.
Same naam, alag kaam β€” yahi Polymorphism hai!

πŸ”΅ Static Polymorphism

Kab decide:Compile Time
Binding:Early Binding
Type:Method Overloading
Kahan:Same class mein
Parameters:ALAG hone chahiye
Extra:Constructor OL bhi

πŸ”΄ Dynamic Polymorphism

Kab decide:Runtime
Binding:Late Binding
Type:Method Overriding
Kahan:Parent-Child mein
Parameters:SAME hone chahiye
Extra:Actual object decides

βœ… Overloading ke 3 Tarike

Count alag: void print(int a) vs void print(int a, int b)
Type alag: void print(int a) vs void print(double a)
Order alag: void show(int a, String b) vs void show(String a, int b)

⚠️ Overriding ke 7 Rules

RuleKya haiStatus
Rule 1Method NAAM same hona chahiyeβœ… Required
Rule 2Parameters SAME (count + type + order)βœ… Required
Rule 3Return type same ya covariantβœ… Required
Rule 4final method β†’ OVERRIDE NAHI!❌ Not Allowed
Rule 5private method β†’ OVERRIDE NAHI!❌ Not Allowed
Rule 6static method β†’ HIDING hoti hai, override nahi❌ Hiding Only
Rule 7Access: same ya MORE visible (narrow nahi!)βœ… Must follow
πŸ”΅ Method Overloading
Static Polymorphism β€” Compile Time pe decide hota hai β€’ Same class mein same naam, alag parameters
1
πŸ“„ MethodOverloading1.java
Basic Method Overloading β€” 6 Versions
Same naam "calculate" ke 6 alag versions β€” parameter count, type aur order teeno tarike use kiye
Static Polymorphism Compile Time
public class MethodOverloading1 {

    // Same naam "calculate" - alag parameters
    // STATIC POLYMORPHISM!

    // 1. Two integers
    static int calculate(int a, int b) {
        System.out.println("calculate(int, int)");
        return a + b;
    }

    // 2. Three integers
    static int calculate(int a, int b, int c) {
        System.out.println("calculate(int, int, int)");
        return a + b + c;
    }

    // 3. Two doubles
    static double calculate(double a, double b) {
        System.out.println("calculate(double, double)");
        return a + b;
    }

    // 4. String and int (order 1)
    static String calculate(String s, int n) {
        System.out.println("calculate(String, int)");
        return s.repeat(n);
    }

    // 5. Int and String (order 2) - ORDER matters!
    static String calculate(int n, String s) {
        System.out.println("calculate(int, String)");
        return n + ": " + s;
    }

    // 6. Double and int
    static double calculate(double d, int n) {
        System.out.println("calculate(double, int)");
        return d * n;
    }

    public static void main(String[] args) {

        System.out.println("=== Static Polymorphism ===");
        System.out.println("Compile time decides which method!\n");

        // Java arguments dekh ke sahi method COMPILE TIME pe choose!
        System.out.println("Result: " + calculate(5, 3));
        System.out.println("Result: " + calculate(5, 3, 2));
        System.out.println("Result: " + calculate(5.5, 3.3));
        System.out.println("Result: " + calculate("Ha", 3));
        System.out.println("Result: " + calculate(1, "One"));
        System.out.println("Result: " + calculate(3.14, 2));

        System.out.println("\n=== RULE ===");
        System.out.println("Signature = name + params (NOT return type)");
        System.out.println("Compile time β†’ method LOCKED (Early Binding)");
    }
}
=== Static Polymorphism ===
Compile time decides which method!

calculate(int, int)
Result: 8
calculate(int, int, int)
Result: 10
calculate(double, double)
Result: 8.8
calculate(String, int)
Result: HaHaHa
calculate(int, String)
Result: 1: One
calculate(double, int)
Result: 6.28

=== RULE ===
Signature = name + params (NOT return type)
Compile time β†’ method LOCKED (Early Binding)

πŸ“– Explanation

Is program mein same naam "calculate" ke 6 alag versions hain. Jab calculate(5, 3) call karte hain, compiler arguments compile time pe hi dekh leta hai aur decide karta hai kaunsa method call hoga.

calculate(5, 3) β†’ dono int hain β†’ calculate(int, int) version call hoga β†’ Result: 8
calculate(5, 3, 2) β†’ teen int hain β†’ calculate(int, int, int) version β†’ Result: 10
calculate("Ha", 3) β†’ String phir int β†’ calculate(String, int) β†’ "Ha".repeat(3) = "HaHaHa"
calculate(1, "One") β†’ int phir String β†’ calculate(int, String) β†’ ORDER matter karta hai!
Return type overloading mein count nahi hoti β€” sirf naam + parameters ka signature check hota hai
2
πŸ“„ MethodOverloading2.java
Type Promotion in Overloading
Java chhote type ko bade type mein automatically promote karta hai — byte→int, char→int, float→double
Type Promotion Ambiguity
public class MethodOverloading2 {

    // Type Promotion in Overloading - IMPORTANT!
    // Java smaller type ko larger mein promote karta hai

    static void show(int n) {
        System.out.println("show(int): " + n);
    }

    static void show(long n) {
        System.out.println("show(long): " + n);
    }

    static void show(double n) {
        System.out.println("show(double): " + n);
    }

    static void show(String s) {
        System.out.println("show(String): " + s);
    }

    // Promotion chain:
    // byte β†’ short β†’ int β†’ long β†’ float β†’ double
    // char β†’ int β†’ long β†’ float β†’ double

    static void demo(int a, double b) {
        System.out.println("demo(int, double): " + a + ", " + b);
    }

    static void demo(double a, int b) {
        System.out.println("demo(double, int): " + a + ", " + b);
    }

    public static void main(String[] args) {

        System.out.println("=== Type Promotion in Overloading ===\n");

        // Exact match
        show(10);          // int    β†’ show(int) exact!
        show(10L);         // long   β†’ show(long) exact!
        show(10.5);        // double β†’ show(double) exact!
        show("Hello");     // String β†’ show(String) exact!

        System.out.println("\n--- Promotion ---");
        byte b = 5;
        show(b);           // byte β†’ promoted to int!

        short s = 100;
        show(s);           // short β†’ promoted to int!

        float f = 3.14f;
        show(f);           // float β†’ promoted to double!

        char c = 'A';
        show(c);           // char β†’ promoted to int! (65)

        System.out.println("\n--- Ambiguity demo ---");
        demo(5, 3.0);      // exact: demo(int, double)
        demo(5.0, 3);      // exact: demo(double, int)

        // demo(5, 3);     // ← AMBIGUOUS ERROR!
        //                    Both match after promotion!
        System.out.println("demo(5,3) would be AMBIGUOUS!");
    }
}
=== Type Promotion in Overloading ===

show(int): 10
show(long): 10
show(double): 10.5
show(String): Hello

--- Promotion ---
show(int): 5
show(int): 100
show(double): 3.140000104904175
show(int): 65

--- Ambiguity demo ---
demo(int, double): 5, 3.0
demo(double, int): 5.0, 3
demo(5,3) would be AMBIGUOUS!

=== Promotion Chain ===
byte→short→int→long→float→double
char→int→long→float→double

πŸ“– Explanation

Java ek chota type automatically bade mein convert (promote) kar deta hai jab exact match nahi milta. Yeh Type Promotion hai.

byte/short β†’ int: Java mein byte aur short ke liye show(int) exist karta hai, isliye promote ho jaate hain
char β†’ int: 'A' ka ASCII value 65 hai, isliye show(c) β†’ show(int): 65 print hota hai
float β†’ double: float ka value approximately print hoga β€” 3.14f β†’ 3.140000104904175
Ambiguity: demo(5, 3) error deta β€” dono methods match karte hain after promotion. Compiler confused!
Promotion Chain: byte→short→int→long→float→double, char→int
πŸ—οΈ Constructor Overloading
Static Polymorphism ka ek type β€” alag parameters se objects banao β€’ this() se chaining possible
1
πŸ“„ ConstructorOverloading1.java
Constructor Chaining β€” 5 Constructors
Student class mein 5 constructors β€” har ek this() se agle ko call karta hai (constructor chaining)
Constructor OL this() Chaining
public class ConstructorOverloading1 {

    // Constructor Overloading = Static Polymorphism!
    static class Student {
        String name;
        int    age;
        String course;
        double gpa;
        String email;

        // Constructor 1: Minimal
        Student(String name) {
            this(name, 18);  // chain!
            System.out.println("C1: name only");
        }

        // Constructor 2: Name + Age
        Student(String name, int age) {
            this(name, age, "General");
            System.out.println("C2: name + age");
        }

        // Constructor 3: Name + Age + Course
        Student(String name, int age, String course) {
            this(name, age, course, 0.0);
            System.out.println("C3: +course");
        }

        // Constructor 4: With GPA
        Student(String name, int age,
                String course, double gpa) {
            this(name, age, course, gpa, "NA");
            System.out.println("C4: +gpa");
        }

        // Constructor 5: MASTER - all fields
        Student(String name, int age, String course,
                double gpa, String email) {
            this.name   = name;
            this.age    = age;
            this.course = course;
            this.gpa    = gpa;
            this.email  = email;
            System.out.println("C5: MASTER constructor");
        }

        void display() {
            System.out.println(name + " | " + age
                + " | " + course + " | " + gpa
                + " | " + email);
        }
    }

    public static void main(String[] args) {

        System.out.println("=== Constructor Overloading ===\n");

        System.out.println("--- Student with name only ---");
        Student s1 = new Student("Rahul");
        s1.display();

        System.out.println("\n--- Student with full details ---");
        Student s2 = new Student("Priya", 20,
            "CS", 3.9, "priya@mail.com");
        s2.display();

        System.out.println("\n--- Student with course ---");
        Student s3 = new Student("Amit", 19, "IT");
        s3.display();
    }
}
=== Constructor Overloading ===

--- Student with name only ---
C5: MASTER constructor
C4: +gpa
C3: +course
C2: name + age
C1: name only
Rahul | 18 | General | 0.0 | NA

--- Student with full details ---
C5: MASTER constructor
Priya | 20 | CS | 3.9 | priya@mail.com

--- Student with course ---
C5: MASTER constructor
C4: +gpa
C3: +course
Amit | 19 | IT | 0.0 | NA

πŸ“– Explanation

Is program mein 5 constructors hain β€” har ek this() se agle ko call karta hai. Yeh Constructor Chaining hai. Output reverse order mein aata hai kyunki pehle innermost complete hota hai.

new Student("Rahul") β†’ C1 β†’ C2 (age=18) β†’ C3 (course="General") β†’ C4 (gpa=0.0) β†’ C5 (email="NA"). Output reverse aata!
this(name, 18) β€” ye C1 se C2 ko call karta hai. this() call hamesha constructor ki PEHLI line honi chahiye
C5 = Master Constructor β€” sirf yahi actual fields set karta hai. Baaki sab chain karte hain
Benefit: Code duplication nahi β€” sirf ek jagah initialization, baaki sab delegate karo
βž• Operator Overloading
Java ka ONLY operator overloading β€” sirf + operator β€’ User-defined nahi hota (C++ mein hota hai)
1
πŸ“„ OperatorOverloading1.java
+ Operator β€” Addition vs Concatenation
+ operator dono numbers par addition karta hai aur koi ek String ho to concatenation β€” left to right evaluation
Operator OL Left-to-Right
public class OperatorOverloading1 {

    public static void main(String[] args) {

        System.out.println("=== + Operator Overloading ===\n");

        // Case 1: Dono numbers β†’ ADDITION
        int a = 10, b = 20;
        System.out.println("int + int = " + (a + b));   // 30

        double x = 5.5, y = 3.3;
        System.out.println("double + double = " + (x + y)); // 8.8

        // Case 2: Koi ek String β†’ CONCATENATION
        String s = "Hello";
        System.out.println("String + int = " + (s + a));    // Hello10
        System.out.println("int + String = " + (a + s));    // 10Hello

        // Case 3: Left to right evaluation
        System.out.println("\n--- Left to right ---");
        System.out.println(10 + 20 + " Result");   // "30 Result"
        System.out.println("Result " + 10 + 20);   // "Result 1020"
        System.out.println("Result " + (10 + 20)); // "Result 30"

        // Why difference?
        // 10 + 20 + " Result"
        //   β†’ (10+20) = 30  β†’ 30 + " Result" = "30 Result"
        // "Result " + 10 + 20
        //   β†’ ("Result "+10) = "Result 10" β†’ "Result 10"+20 = "Result 1020"

        System.out.println("\n--- char + char ---");
        char c1 = 'A', c2 = 'B';
        System.out.println(c1 + c2);         // 65+66=131 (int!)
        System.out.println("" + c1 + c2);    // "AB" (String!)

        System.out.println("\n=== Java does NOT support ===");
        System.out.println("No -, *, /, ** overloading");
        System.out.println("No user-defined operator overloading");
        System.out.println("C++ supports it, Java DOES NOT!");
    }
}
=== + Operator Overloading ===

int + int = 30
double + double = 8.8
String + int = Hello10
int + String = 10Hello

--- Left to right ---
30 Result
Result 1020
Result 30

--- char + char ---
131
AB

=== Java does NOT support ===
No -, *, /, ** overloading
No user-defined operator overloading
C++ supports it, Java DOES NOT!

πŸ“– Explanation

+ operator Java mein internally overloaded hai β€” sirf + ke liye, aur baaki operators ke liye nahi!

Dono numbers: 10 + 20 = 30 (arithmetic addition)
Koi ek String: "Hello" + 10 = "Hello10" (string concatenation)
Tricky β€” Left to Right: "Result " + 10 + 20 β†’ pehle "Result " + 10 = "Result 10", phir + 20 = "Result 1020"
char + char = int! 'A'=65, 'B'=66, to 65+66=131. String banana ho to "" + c1 + c2 likho!
C++ mein user apne operators define kar sakta hai β€” Java mein NAHI hota
πŸ”΄ Method Overriding
Dynamic Polymorphism β€” Runtime pe decide hota hai β€’ Parent-Child mein same signature se rewrite
1
πŸ“„ MethodOverriding1.java
Animal Hierarchy β€” Basic Overriding
Dog, Cat, Bird sab Animal extend karte hain β€” sound() aur move() override karte hain β€” runtime pe actual object decide karta hai
Dynamic Polymorphism Late Binding
public class MethodOverriding1 {

    // DYNAMIC POLYMORPHISM - Runtime decides!
    static class Animal {
        String name;

        Animal(String name) {
            this.name = name;
        }

        void sound() {
            System.out.println(name + ": Generic animal sound");
        }

        void move() {
            System.out.println(name + ": Moving somehow");
        }

        void eat() {
            System.out.println(name + ": Eating food");
        }
    }

    static class Dog extends Animal {
        Dog(String name) { super(name); }

        // OVERRIDING - same signature!
        @Override
        void sound() {
            System.out.println(name + ": Woof Woof!");
        }

        @Override
        void move() {
            System.out.println(name + ": Running on 4 legs");
        }
        // eat() NOT overridden β†’ parent version used
    }

    static class Cat extends Animal {
        Cat(String name) { super(name); }

        @Override
        void sound() {
            System.out.println(name + ": Meow Meow!");
        }

        @Override
        void move() {
            System.out.println(name + ": Sneaking silently");
        }
    }

    static class Bird extends Animal {
        Bird(String name) { super(name); }

        @Override
        void sound() {
            System.out.println(name + ": Tweet Tweet!");
        }

        @Override
        void move() {
            System.out.println(name + ": Flying in sky");
        }
    }

    public static void main(String[] args) {

        System.out.println("=== DYNAMIC POLYMORPHISM ===");
        System.out.println("Runtime decides which method runs!\n");

        // ALL stored as Animal reference (upcasting)
        Animal[] animals = {
            new Dog("Tommy"),
            new Cat("Whiskers"),
            new Bird("Tweety"),
            new Dog("Bruno"),
            new Cat("Luna")
        };

        System.out.println("--- sound() - same call, different output ---");
        for (Animal a : animals) {
            a.sound(); // RUNTIME decides! Each object's version!
        }

        System.out.println("\n--- move() ---");
        for (Animal a : animals) {
            a.move();
        }

        System.out.println("\n--- eat() [not overridden] ---");
        for (Animal a : animals) {
            a.eat(); // ALL use Animal's version
        }

        System.out.println("\n=== KEY POINT ===");
        System.out.println("a.sound() β†’ same CODE LINE");
        System.out.println("But DIFFERENT output for each object!");
    }
}
=== DYNAMIC POLYMORPHISM ===
Runtime decides which method runs!

--- sound() - same call, different output ---
Tommy: Woof Woof!
Whiskers: Meow Meow!
Tweety: Tweet Tweet!
Bruno: Woof Woof!
Luna: Meow Meow!

--- move() ---
Tommy: Running on 4 legs
Whiskers: Sneaking silently
Tweety: Flying in sky
Bruno: Running on 4 legs
Luna: Meow Meow!

--- eat() [not overridden] ---
Tommy: Eating food
Whiskers: Eating food
Tweety: Eating food
Bruno: Eating food
Luna: Eating food

=== KEY POINT ===
a.sound() β†’ same CODE LINE
But DIFFERENT output for each object!
Runtime checks actual object type β†’ LATE BINDING

πŸ“– Explanation

Yahi hai Dynamic Polymorphism ka magic! Ek hi line a.sound() β€” alag alag output! Compiler nahi, runtime decide karta hai.

Animal[] animals β€” sab Animal reference mein store, lekin actual objects alag (Dog, Cat, Bird)
Loop mein a.sound() β€” ek hi line, lekin Dog ka "Woof", Cat ka "Meow", Bird ka "Tweet" β€” LATE BINDING!
eat() Dog/Cat/Bird ne override nahi kiya β€” isliye sab ka Animal wala version chala
Yeh Upcasting hai β€” Dog object ko Animal reference mein store karna valid hai (IS-A relationship)
2
πŸ“„ MethodOverriding2.java
Overriding Rules β€” Final, Static, Private
Overriding ke saare rules β€” final override nahi, static hiding hai, private visible nahi, access modifier rules
Rules Demo Static Hiding
public class MethodOverriding2 {

    static class Parent {

        // Rule 1: public β†’ child can make public or keep public
        public void publicMethod() {
            System.out.println("Parent: publicMethod");
        }

        // Rule 2: protected β†’ child can make protected or public
        protected void protectedMethod() {
            System.out.println("Parent: protectedMethod");
        }

        // Rule 4: final β†’ CANNOT override!
        final void finalMethod() {
            System.out.println("Parent: finalMethod (FINAL)");
        }

        // Rule 5: static β†’ NOT overriding (HIDING!)
        static void staticMethod() {
            System.out.println("Parent: staticMethod");
        }

        // private β†’ NOT visible to child, cannot override
        private void privateMethod() {
            System.out.println("Parent: privateMethod");
        }

        int getValue() { return 100; }
    }

    static class Child extends Parent {

        // βœ… Rule 1: public stays public - OK
        public void publicMethod() {
            System.out.println("Child: publicMethod (overridden)");
        }

        // βœ… Rule 2: protected β†’ public is MORE visible - OK
        public void protectedMethod() {
            System.out.println("Child: protectedMethod (more visible)");
        }

        // ❌ Rule 3 violation (commented - would cause error):
        // protected void mustBePublic() { } ← ERROR! Less visible!

        // ❌ finalMethod() cannot override (commented):
        // void finalMethod() { } ← COMPILE ERROR!

        // static β†’ This is METHOD HIDING, NOT overriding!
        static void staticMethod() {
            System.out.println("Child: staticMethod (HIDING)");
        }

        // private β†’ New method in child, not override!
        private void privateMethod() {
            System.out.println("Child: privateMethod (NEW, not override)");
        }

        // βœ… Same return type - OK
        int getValue() { return 200; }
    }

    public static void main(String[] args) {

        System.out.println("=== Overriding Rules Demo ===\n");

        Child child   = new Child();
        Parent parent = child; // upcast

        System.out.println("--- Instance methods (overriding) ---");
        parent.publicMethod();     // Child's version (overriding!)
        parent.protectedMethod();  // Child's version
        parent.finalMethod();      // ALWAYS Parent's (final!)
        System.out.println("getValue: " + parent.getValue()); // 200

        System.out.println("\n--- Static methods (hiding) ---");
        Parent.staticMethod(); // Parent's (class reference)
        Child.staticMethod();  // Child's (class reference)
        parent.staticMethod(); // Parent's! (reference type decides for static)
        child.staticMethod();  // Child's

        System.out.println("\n=== OVERRIDING vs HIDING ===");
        System.out.println("Instance method: actual OBJECT decides β†’ overriding");
        System.out.println("Static method:   REFERENCE TYPE decides β†’ hiding");
    }
}
=== Overriding Rules Demo ===

--- Instance methods (overriding) ---
Child: publicMethod (overridden)
Child: protectedMethod (more visible)
Parent: finalMethod (FINAL)
getValue: 200

--- Static methods (hiding) ---
Parent: staticMethod
Child: staticMethod
Parent: staticMethod
Child: staticMethod

=== OVERRIDING vs HIDING ===
Instance method: actual OBJECT decides β†’ overriding
Static method:   REFERENCE TYPE decides β†’ hiding

πŸ“– Explanation

Rules clearly dikhate hain kya hota hai jab alag access modifiers aur keywords use hote hain.

parent.publicMethod() β†’ Child ka version β€” kyunki actual object Child hai (LATE BINDING)
parent.finalMethod() β†’ hamesha Parent ka β€” final method override ho hi nahi sakta!
parent.staticMethod() β†’ Parent ka β€” static ke liye reference type decide karta hai, object nahi β€” yeh HIDING hai
Access modifier: Parent ke public ko Child protected nahi kar sakta β€” ERROR! Sirf same ya zyada visible ho sakta hai
private method child mein visible hi nahi β€” Child mein likhna = NEW method, override nahi!
πŸ”„ Covariant Return Type
Java 5+ feature β€” Override karte waqt return type parent ka subtype ho sakta hai
1
πŸ“„ CovariantReturn1.java
Animal β†’ Dog β†’ GoldenRetriever Covariant Chain
Animal.getInstance() returns Animal, Dog.getInstance() returns Dog, GoldenRetriever.getInstance() returns GoldenRetriever β€” no cast needed!
Covariant Return Java 5+
public class CovariantReturn1 {

    static class Animal {
        String name;
        Animal(String name) { this.name = name; }

        // Returns Animal type
        Animal getInstance() {
            System.out.println("Animal.getInstance()");
            return new Animal("Generic Animal");
        }

        void describe() {
            System.out.println("Animal: " + name);
        }
    }

    static class Dog extends Animal {
        String breed;

        Dog(String name, String breed) {
            super(name);
            this.breed = breed;
        }

        // COVARIANT RETURN: returns Dog instead of Animal!
        // Dog IS-A Animal β†’ covariant allowed!
        @Override
        Dog getInstance() {
            System.out.println("Dog.getInstance()");
            return new Dog("New Dog", "Labrador");
        }

        @Override
        void describe() {
            System.out.println("Dog: " + name
                + " (" + breed + ")");
        }

        void bark() {
            System.out.println(name + ": Woof!");
        }
    }

    static class GoldenRetriever extends Dog {
        GoldenRetriever(String name) {
            super(name, "Golden Retriever");
        }

        // Further covariant: returns GoldenRetriever
        @Override
        GoldenRetriever getInstance() {
            System.out.println("GoldenRetriever.getInstance()");
            return new GoldenRetriever("Buddy");
        }

        @Override
        void describe() {
            System.out.println("GoldenRetriever: " + name);
        }
    }

    public static void main(String[] args) {

        System.out.println("=== Covariant Return Type ===\n");

        Animal  a = new Animal("Generic");
        Dog     d = new Dog("Tommy", "Poodle");
        GoldenRetriever g = new GoldenRetriever("Max");

        System.out.println("--- getInstance() calls ---");
        Animal  result1 = a.getInstance();
        result1.describe();

        Dog     result2 = d.getInstance();
        result2.describe();
        result2.bark(); // ← Direct Dog method! No cast needed!

        GoldenRetriever result3 = g.getInstance();
        result3.describe();

        System.out.println("\n--- Polymorphism with covariant ---");
        Animal[] animals = { a, d, g };
        for (Animal animal : animals) {
            Animal result = animal.getInstance();
            result.describe();
        }

        System.out.println("\n=== BENEFIT ===");
        System.out.println("Without covariant: (Dog) a.getInstance() β†’ cast needed!");
        System.out.println("With covariant:    d.getInstance() β†’ returns Dog directly!");
    }
}
=== Covariant Return Type ===

--- getInstance() calls ---
Animal.getInstance()
Animal: Generic Animal
Dog.getInstance()
Dog: New Dog (Labrador)
New Dog: Woof!
GoldenRetriever.getInstance()
GoldenRetriever: Buddy

--- Polymorphism with covariant ---
Animal.getInstance()
Animal: Generic Animal
Dog.getInstance()
Dog: New Dog (Labrador)
GoldenRetriever.getInstance()
GoldenRetriever: Buddy

=== BENEFIT ===
Without covariant: (Dog) a.getInstance() β†’ cast needed!
With covariant:    d.getInstance() β†’ returns Dog directly!

πŸ“– Explanation

Covariant Return Type β€” Override karte waqt return type ko parent ke return type ka subtype bana sakte hain.

Animal.getInstance() β†’ Animal return karta, Dog.getInstance() β†’ Dog return karta (Dog IS-A Animal β€” allowed!)
Benefit: Dog result2 = d.getInstance() β€” seedha Dog milta, (Dog) cast nahi karna padta!
result2.bark() directly call ho sakta β€” kyunki result2 ka type Dog hai, Animal nahi
Chain: GoldenRetriever β†’ Dog β†’ Animal β€” har level pe return type aur specific hota jaata hai
⚑ Runtime Polymorphism Demo
Shape hierarchy ke through runtime polymorphism ka real-world example
1
πŸ“„ RuntimePolymorphism1.java
Shape Calculator β€” Real World Demo
Circle, Rectangle, Triangle sab Shape extend karte hain β€” getArea() override karte hain β€” ek loop mein sab handle
Runtime Polymorphism Real World
public class RuntimePolymorphism1 {

    static class Shape {
        String color;

        Shape(String color) {
            this.color = color;
        }

        double getArea() { return 0; }
        double getPerimeter() { return 0; }
        String getType() { return "Shape"; }

        void display() {
            System.out.printf(
                "%-20s color=%-8s area=%8.2f  perim=%8.2f%n",
                getType(), color, getArea(), getPerimeter());
        }
    }

    static class Circle extends Shape {
        double radius;

        Circle(String color, double radius) {
            super(color);
            this.radius = radius;
        }

        @Override
        double getArea() {
            return Math.PI * radius * radius;
        }

        @Override
        double getPerimeter() {
            return 2 * Math.PI * radius;
        }

        @Override
        String getType() { return "Circle(r=" + radius + ")"; }
    }

    static class Rectangle extends Shape {
        double l, w;

        Rectangle(String color, double l, double w) {
            super(color);
            this.l = l; this.w = w;
        }

        @Override double getArea()      { return l * w; }
        @Override double getPerimeter() { return 2 * (l + w); }
        @Override String getType() {
            return "Rect(" + l + "x" + w + ")";
        }
    }

    static class Triangle extends Shape {
        double a, b, c;

        Triangle(String color, double a, double b, double c) {
            super(color);
            this.a = a; this.b = b; this.c = c;
        }

        @Override
        double getPerimeter() { return a + b + c; }

        @Override
        double getArea() {
            double s = getPerimeter() / 2;
            return Math.sqrt(s*(s-a)*(s-b)*(s-c));
        }

        @Override
        String getType() {
            return "Triangle("+a+","+b+","+c+")";
        }
    }

    // Works with ANY Shape subclass - polymorphism!
    static void printReport(Shape[] shapes) {
        System.out.printf("%-20s %-10s %-12s %-12s%n",
            "Type", "Color", "Area", "Perimeter");
        System.out.println("-".repeat(56));

        double totalArea = 0;
        for (Shape s : shapes) {
            s.display(); // RUNTIME decides which display/getArea!
            totalArea += s.getArea();
        }

        System.out.println("-".repeat(56));
        System.out.printf("Total Area: %.2f%n", totalArea);
    }

    static Shape getLargest(Shape[] shapes) {
        Shape largest = shapes[0];
        for (Shape s : shapes) {
            if (s.getArea() > largest.getArea()) {
                largest = s;
            }
        }
        return largest;
    }

    public static void main(String[] args) {

        Shape[] shapes = {
            new Circle("Red", 5),
            new Rectangle("Blue", 10, 4),
            new Triangle("Green", 3, 4, 5),
            new Circle("Yellow", 3),
            new Rectangle("Purple", 7, 2),
            new Triangle("Orange", 5, 5, 5)
        };

        System.out.println("=== Runtime Polymorphism ===\n");
        printReport(shapes);

        System.out.println("\nLargest shape: "
            + getLargest(shapes).getType());
    }
}
=== Runtime Polymorphism ===

Type                 Color      Area         Perimeter
--------------------------------------------------------
Circle(r=5.0)        color=Red      area=    78.54  perim=    31.42
Rect(10.0x4.0)       color=Blue     area=    40.00  perim=    28.00
Triangle(3.0,4.0,5.0) color=Green    area=     6.00  perim=    12.00
Circle(r=3.0)        color=Yellow   area=    28.27  perim=    18.85
Rect(7.0x2.0)        color=Purple   area=    14.00  perim=    18.00
Triangle(5.0,5.0,5.0) color=Orange   area=    10.83  perim=    15.00
--------------------------------------------------------
Total Area: 177.64

Largest shape: Circle(r=5.0)

πŸ“– Explanation

Yeh hai Runtime Polymorphism ka real-world power! printReport(Shape[]) function β€” koi bhi Shape subclass do, hamesha kaam karega!

s.getArea() β€” same call, lekin runtime pe Circle PI*rΒ², Rectangle l*w, Triangle Heron's formula use karta hai
printReport() function Shape array leta hai β€” naya shape class add karo, function change karne ki zaroorat nahi!
getLargest() bhi Shape return karta β€” polymorphism se koi bhi shape largest ho sakta hai
Yahi hai OOP ka fayda β€” extensible code jisme nayi shapes add karna aasaan hai
🎯 Complete Combined Program
Ek hi program mein dono types β€” Static aur Dynamic Polymorphism
1
πŸ“„ PolymorphismComplete1.java
Calculator β€” Both Polymorphism Types Together
Calculator hierarchy mein Overloading (Static) aur Overriding (Dynamic) dono demonstrate kiye gaye hain
Static + Dynamic Complete Demo
public class PolymorphismComplete1 {

    // BOTH types of polymorphism in ONE program!

    static class Calculator {
        String brand;

        Calculator(String brand) {
            this.brand = brand;
            System.out.println(brand + " calculator created");
        }

        // ===== STATIC POLYMORPHISM (Overloading) =====
        double calculate(double a, double b) {
            System.out.print("[BASIC] ");
            return a + b;
        }

        double calculate(double a, double b, char op) {
            System.out.print("[BASIC-OP] ");
            switch(op) {
                case '+': return a + b;
                case '-': return a - b;
                case '*': return a * b;
                case '/': return b != 0 ? a/b : 0;
                default:  return 0;
            }
        }

        double calculate(int[] nums) {
            System.out.print("[BASIC-ARR] ");
            int sum = 0;
            for (int n : nums) sum += n;
            return sum;
        }

        // ===== DYNAMIC POLYMORPHISM base =====
        String getType() { return "Basic"; }

        void showInfo() {
            System.out.println("[" + getType() + "] " + brand);
        }
    }

    static class ScientificCalculator
            extends Calculator {

        ScientificCalculator(String brand) {
            super(brand);
        }

        // STATIC POLYMORPHISM: More overloads
        double calculate(double base, int exp) {
            System.out.print("[SCI-POW] ");
            return Math.pow(base, exp);
        }

        double calculate(double angle, String trigFunc) {
            System.out.print("[SCI-TRIG] ");
            switch(trigFunc.toLowerCase()) {
                case "sin": return Math.sin(Math.toRadians(angle));
                case "cos": return Math.cos(Math.toRadians(angle));
                case "tan": return Math.tan(Math.toRadians(angle));
                default:    return 0;
            }
        }

        // DYNAMIC POLYMORPHISM: Override
        @Override
        String getType() { return "Scientific"; }

        @Override
        double calculate(double a, double b) {
            System.out.print("[SCI-BASIC] ");
            return super.calculate(a, b);
        }
    }

    static class ProgrammerCalculator
            extends ScientificCalculator {

        ProgrammerCalculator(String brand) {
            super(brand);
        }

        // New overload
        String calculate(int n, String base) {
            System.out.print("[PROG-CONV] ");
            switch(base.toLowerCase()) {
                case "binary": return Integer.toBinaryString(n);
                case "hex":    return Integer.toHexString(n).toUpperCase();
                case "octal":  return Integer.toOctalString(n);
                default:       return String.valueOf(n);
            }
        }

        @Override
        String getType() { return "Programmer"; }
    }

    public static void main(String[] args) {

        System.out.println("=== BOTH Polymorphism Types ===\n");

        // ===== STATIC POLYMORPHISM =====
        System.out.println("--- Static Polymorphism (Overloading) ---");
        ScientificCalculator sci = new ScientificCalculator("Casio");

        System.out.printf("5+3    = %.0f%n", sci.calculate(5, 3));
        System.out.printf("5*3    = %.0f%n", sci.calculate(5, 3, '*'));
        System.out.printf("2^8    = %.0f%n", sci.calculate(2.0, 8));
        System.out.printf("sin30  = %.4f%n", sci.calculate(30, "sin"));
        System.out.printf("Array  = %.0f%n", sci.calculate(new int[]{1,2,3,4,5}));

        // ===== DYNAMIC POLYMORPHISM =====
        System.out.println("\n--- Dynamic Polymorphism (Overriding) ---");

        // All stored as Calculator (upcasting)
        Calculator[] calcs = {
            new Calculator("Basic-Brand"),
            new ScientificCalculator("Casio-Sci"),
            new ProgrammerCalculator("TI-Prog")
        };

        for (Calculator c : calcs) {
            c.showInfo();   // RUNTIME decides getType()!
        }

        ProgrammerCalculator prog = new ProgrammerCalculator("TI");
        System.out.println("\n255 in binary: " + prog.calculate(255, "binary"));
        System.out.println("255 in hex:    " + prog.calculate(255, "hex"));
        System.out.println("255 in octal:  " + prog.calculate(255, "octal"));
    }
}
=== BOTH Polymorphism Types ===

--- Static Polymorphism (Overloading) ---
Casio calculator created
[SCI-BASIC] [BASIC] 5+3    = 8
[BASIC-OP] 5*3    = 15
[SCI-POW] 2^8    = 256
[SCI-TRIG] sin30  = 0.5000
[BASIC-ARR] Array  = 15

--- Dynamic Polymorphism (Overriding) ---
Basic-Brand calculator created
Casio-Sci calculator created
TI-Prog calculator created
[Basic] Basic-Brand
[Scientific] Casio-Sci
[Programmer] TI-Prog

TI calculator created
[PROG-CONV] 255 in binary: 11111111
[PROG-CONV] 255 in hex:    FF
[PROG-CONV] 255 in octal:  377

πŸ“– Explanation

Is program mein dono types clearly alag alag sections mein dikhaye gaye hain.

Static Part: sci.calculate() β€” compiler compile time pe decide karta hai β€” (5,3) = double,double, (5,3,'*') = double,double,char, (2.0,8) = double,int
Dynamic Part: c.showInfo() β€” runtime pe getType() decide hota β€” Calculator ke liye "Basic", Scientific ke liye "Scientific"
ScientificCalculator mein calculate(double a, double b) override kiya β€” plus super.calculate() call bhi kiya
ProgrammerCalculator 255 ko binary/hex/octal mein convert karta hai β€” Integer utility methods use
πŸ“‹ Complete Summary
Saari important baatein ek jagah

πŸ”΅ Static Polymorphism

β€’Compile Time decide
β€’Early Binding
β€’Method Overloading
β€’Same class mein
β€’Same naam, alag params
β€’Constructor OL bhi
β€’Return type count nahi

πŸ”΄ Dynamic Polymorphism

β€’Runtime decide
β€’Late Binding
β€’Method Overriding
β€’Parent-Child mein
β€’Same naam, same params
β€’Actual object decides
β€’@Override annotation

🚫 Override Nahi Hota

β€’final method
β€’private method
β€’static method (hiding hoti)
β€’Constructor override nahi
β€’Variables override nahi

βž• Operator Overloading

β€’Sirf + operator internally
β€’Numbers β†’ Addition
β€’String β†’ Concatenation
β€’Left to right evaluate
β€’char+char = int!
β€’User-defined NAHI Java mein
⚠️ Common Mistakes
Exam mein yahi galtiyan sabse zyada hoti hain
MistakeWrong ThinkingReality
Return type overloading int f() aur double f() overloaded hain NOT overloading β€” Compile Error!
Static override static method override hota hai Static HIDES, reference type decides!
private override private method override hota hai Private child mein visible nahi!
Access narrowing public parent β†’ protected child OK NOT ALLOWED β€” Compile Error!
Variables override Variables bhi override ho sakte hain Variables NEVER override! Only methods!
char + char "AB" aayega 65+66=131 (int addition!)

πŸ† Promotion Chain β€” Yaad Karo

Numeric: byte β†’ short β†’ int β†’ long β†’ float β†’ double
Char: char β†’ int β†’ long β†’ float β†’ double
Covariant Return: Child ka return type = Parent return type ka subtype (Java 5+)
πŸ’¬ Interview Questions
Click karo expand karne ke liye β€” exam mein yahi poochhe jaate hain
Q1 Overloading aur Overriding mein kya difference hai? β€Ί
Overloading: Same CLASS mein, same naam, ALAG params. COMPILE TIME decide karta hai. Static Polymorphism.

Overriding: Parent-CHILD mein, same naam, SAME params. RUNTIME decide karta hai. Dynamic Polymorphism.

void show(int a) aur void show(double a) = Overloading (same class)
class Child { void show() {...} } parent ke void show() pe = Overriding
Q2 Overloading mein return type kyun nahi check hota? β€Ί
Method Signature = Method naam + Parameter list (return type NAHI hota).

Jab calculate(5, 3) call hota hai, compiler arguments ki type dekh ke decide karta hai.
Return type caller ko pata hota hai lekin compiler call resolution ke liye parameters hi dekh sakta hai.

Isliye int f() aur double f() β†’ COMPILE ERROR β€” same signature!
Q3 Static method override hota hai ya nahi? β€Ί
NAHI! Static methods HIDE hote hain, override nahi.

Override: Actual OBJECT type decide karta hai β†’ Late Binding
Hiding: REFERENCE TYPE decide karta hai β†’ Early Binding

Parent p = new Child();
p.instanceMethod() β†’ Child ka version (overriding!)
p.staticMethod() β†’ Parent ka version! (reference type = Parent)
Q4 private method override hota hai? β€Ί
NAHI! Private method child class mein visible hi nahi hota.

Child mein same naam se private method likhna = NEW method, override nahi.
Runtime pe parent ka private chalta, child ka nahi.

@Override annotation lagaoge to Compile Error aayega!
Q5 Covariant Return Type kya hai? β€Ί
Override karte waqt, return type parent ke return type ka subtype ho sakta hai (Java 5+).

Animal getInstance() parent mein β†’ Dog getInstance() child mein β€” allowed because Dog IS-A Animal!

Benefit: Dog result = d.getInstance() β†’ cast nahi karna pada!
Without covariant: (Dog) a.getInstance() β†’ explicit cast zaroori tha.
Q6 Method Hiding kya hai? β€Ί
Jab child class mein parent ke same signature wala static method likhte hain, to woh hiding hai, overriding nahi.

Overriding = actual object type decides (runtime)
Hiding = reference type decides (compile time)

Parent p = new Child();
p.staticMethod() β†’ Parent ka static method! (hiding β€” reference type = Parent)
Q7 Early Binding aur Late Binding kya hota hai? β€Ί
Early Binding (Static): Method call compile time pe hi resolve ho jaata hai. Overloading mein hota. Method address compile time pe fix ho jaati.

Late Binding (Dynamic): Method call runtime pe resolve hota hai. Overriding mein hota. Actual object ki type dekh ke runtime pe decide hota.

Early = Fast, Late = Flexible (polymorphism ka fayda)
Q8 Java mein Operator Overloading kyun support nahi? β€Ί
Java designers ne simplicity ke liye user-defined operator overloading hataya.

C++ mein possible: Vector operator+(Vector v)
Java mein nahi β€” code readability aur simplicity maintain karne ke liye.

Java mein sirf + internally overloaded hai:
Numbers β†’ Addition, Any String β†’ Concatenation