JAVA DEEP DIVE

☕ Java Type Casting

User-Defined Type Casting — Upcasting · Downcasting · instanceof — Complete Revision Guide (Hinglish)

🟢 Upcasting
Child → Parent  |  Automatic/Implicit
🔴 Downcasting
Parent → Child  |  Explicit (manual)
⚡ instanceof
Runtime type check — safe downcast ke liye

🧠 User-Defined Type Casting Kya Hai?

Condition: Casting tabhi possible hai jab extends ya implements relationship HO!

class Dog extends Animal { } → relationship hai → casting valid!
class Cat { } → Dog se koi relation nahi → (Cat) dog → ERROR!

Most Important Rule: OBJECT NAHI BADLA — sirf reference type badla! Same memory address, different reference.

🧠 Memory Visualization
Dog d = new Dog("Tommy", "Lab"); HEAP: ┌────────────────────────────────┐ │ DOG OBJECT [addr:1000]│ │ ┌─────────────────────────┐ │ │ │ ANIMAL PART (parent) │ │ │ │ String name │ │ │ │ void eat() │ │ │ ├─────────────────────────┤ │ │ │ DOG PART (child) │ │ │ │ String breed │ │ │ │ void bark() │ │ │ └─────────────────────────┘ │ └────────────────────────────────┘ d ──────→ 1000 (Dog reference) Animal a = d; ← UPCASTING a ──────→ 1000 (SAME object! Animal reference) OBJECT NAHI BADLA! Sirf reference type badla!
🟢

Upcasting — Child to Parent

Implicit · Automatic · No cast operator needed

🧠 Concept — Upcasting Kyu Automatic Hai?

"Dog IS-A Animal" → hamesha true! Isliye Java automatically allow karta hai — koi explicit cast operator nahi chahiye.

After upcasting:
a.eat() → ✅ Animal method accessible
a.bark() → ❌ ERROR! Animal reference bark() nahi jaanta — even though actual object Dog hai!
a.describe() → ✅ Dog ka version chala (Overriding!)

Golden Rule: Reference type decide karta hai kaunse methods accessible hain. Actual object type decide karta hai kaunsa version chalega.

PROGRAM 1 Upcasting1.java — Basic Upcasting
Implicit Cast + Overriding
public class Upcasting1 {

    static class Animal {
        String name;

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

        void eat()     { System.out.println(name + " is eating"); }
        void breathe() { System.out.println(name + " is breathing"); }

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

    static class Dog extends Animal {
        String breed;

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

        void bark()  { System.out.println(name + " says: Woof!"); }
        void fetch() { System.out.println(name + " fetches the ball!"); }

        void describe() {
            System.out.println("I am a Dog: "
                + name + " (" + breed + ")");
        }
    }

    public static void main(String[] args) {

        Dog dog = new Dog("Tommy", "Labrador");
        System.out.println("--- Dog reference ---");
        dog.eat();
        dog.bark();    // ✅ Dog method accessible
        dog.fetch();   // ✅ Dog method accessible
        dog.describe();

        System.out.println("\n--- Upcasting: Animal a = dog ---");
        // UPCASTING - implicit, no cast operator needed!
        Animal a = dog;  // same object, Animal reference now!

        a.eat();          // ✅ Animal method
        a.breathe();     // ✅ Animal method
        // a.bark();     // ❌ ERROR! Animal ref nahi jaanta bark()
        // a.fetch();    // ❌ ERROR! Animal ref nahi jaanta fetch()
        a.describe();    // ✅ calls Dog's describe! (Overriding!)

        System.out.println("\n--- Same object? ---");
        System.out.println("dog == a : " + (dog == a));
        // TRUE! Same memory address!

        System.out.println("\n--- Direct upcasting ---");
        Animal a2 = new Dog("Bruno", "German Shepherd");
        a2.eat();
        a2.describe(); // Dog's version (overriding!)
        // a2.bark();  // ❌ not accessible through Animal ref
    }
}
⚡ Output
--- Dog reference ---
Tommy is eating
Tommy says: Woof!
Tommy fetches the ball!
I am a Dog: Tommy (Labrador)

--- Upcasting: Animal a = dog ---
Tommy is eating
Tommy is breathing
I am a Dog: Tommy (Labrador)

--- Same object? ---
dog == a : true

--- Direct upcasting ---
Bruno is eating
I am a Dog: Bruno (German Shepherd)
💬 Explanation: Animal a = dog — same object, sirf reference type badla! a.bark() error dega — Animal reference bark() nahi jaanta. But a.describe() → Dog ka describe chala — kyunki runtime pe actual object Dog hai! dog == a → true — same memory address! Yahi polymorphism ka base hai!
PROGRAM 2 Upcasting2.java — Shapes Polymorphism
Array + Method Parameter Upcasting
public class Upcasting2 {

    static class Shape {
        String color;
        Shape(String color) { this.color = color; }
        double getArea() { return 0; }
        void display() {
            System.out.printf("%-12s color=%-8s area=%.2f%n",
                getClass().getSimpleName(), color, getArea());
        }
    }

    static class Circle extends Shape {
        double radius;
        Circle(String color, double radius) {
            super(color); this.radius = radius;
        }
        double getArea() { return Math.PI * radius * radius; }
    }

    static class Rectangle extends Shape {
        double length, width;
        Rectangle(String color, double l, double w) {
            super(color); this.length = l; this.width = w;
        }
        double getArea() { return length * width; }
    }

    static class Triangle extends Shape {
        double base, height;
        Triangle(String color, double b, double h) {
            super(color); this.base = b; this.height = h;
        }
        double getArea() { return 0.5 * base * height; }
    }

    // Upcasting in method parameter!
    static void printShape(Shape s) {
        s.display(); // s can be ANY child object!
    }

    static double totalArea(Shape[] shapes) {
        double total = 0;
        for (Shape s : shapes) {
            total += s.getArea(); // each calls own getArea()!
        }
        return total;
    }

    public static void main(String[] args) {

        // UPCASTING - all stored as Shape!
        Shape[] shapes = {
            new Circle("Red", 5),         // upcast!
            new Rectangle("Blue", 8, 4), // upcast!
            new Triangle("Green", 6, 3), // upcast!
            new Circle("Yellow", 3),      // upcast!
            new Rectangle("Pink", 5, 5)  // upcast!
        };

        for (Shape s : shapes) {
            printShape(s); // each calls its own display/getArea!
        }

        System.out.printf("%nTotal Area: %.2f%n", totalArea(shapes));
    }
}
⚡ Output
Circle       color=Red      area=78.54
Rectangle    color=Blue     area=32.00
Triangle     color=Green    area=9.00
Circle       color=Yellow   area=28.27
Rectangle    color=Pink     area=25.00

Total Area: 172.81
💬 Explanation: Shape[] array mein Circle, Rectangle, Triangle sab — yahi upcasting ki real power hai! totalArea(Shape[]) ek method har type handle karta hai. Runtime pe har object apna getArea() call karta hai. One array holds ALL types — yahi polymorphism hai!
PROGRAM 3 Upcasting3.java — Interface Upcasting
Interface + implements + Upcasting
public class Upcasting3 {

    interface Payable {
        double getAmount();
        void processPayment();
    }

    static class CreditCard implements Payable {
        String cardNo;
        double amount;

        CreditCard(String cardNo, double amount) {
            this.cardNo = cardNo; this.amount = amount;
        }

        public double getAmount() { return amount; }

        public void processPayment() {
            System.out.println("[CREDIT] Card: "
                + cardNo.substring(0,4) + "****"
                + " Amount: Rs." + amount);
        }

        void applyRewards() {
            System.out.println("Rewards: "
                + (amount * 0.02) + " points!");
        }
    }

    static class UPI implements Payable {
        String upiId; double amount;

        UPI(String upiId, double amount) {
            this.upiId = upiId; this.amount = amount;
        }

        public double getAmount() { return amount; }

        public void processPayment() {
            System.out.println("[UPI] ID: " + upiId
                + " Amount: Rs." + amount);
        }
    }

    static class Cash implements Payable {
        double amount;
        Cash(double amount) { this.amount = amount; }
        public double getAmount() { return amount; }
        public void processPayment() {
            System.out.println("[CASH] Amount: Rs." + amount);
        }
    }

    // Upcasting → accepts ANY Payable!
    static void checkout(Payable payment) {
        System.out.println("Processing payment...");
        payment.processPayment();
        System.out.println("Amount: Rs." + payment.getAmount());
        System.out.println("Done!\n");
    }

    public static void main(String[] args) {

        // UPCASTING to interface type!
        Payable p1 = new CreditCard("4567890123456789", 1500);
        Payable p2 = new UPI("rahul@upi", 800);
        Payable p3 = new Cash(500);

        checkout(p1);
        checkout(p2);
        checkout(p3);

        // p1.applyRewards(); ← ERROR! Payable ref nahi jaanta
        Payable[] payments = { p1, p2, p3 };
        double total = 0;
        for (Payable p : payments) total += p.getAmount();
        System.out.println("Total: Rs." + total);
    }
}
⚡ Output
Processing payment...
[CREDIT] Card: 4567**** Amount: Rs.1500.0
Amount: Rs.1500.0
Done!

Processing payment...
[UPI] ID: rahul@upi Amount: Rs.800.0
Amount: Rs.800.0
Done!

Processing payment...
[CASH] Amount: Rs.500.0
Amount: Rs.500.0
Done!

Total: Rs.2800.0
💬 Explanation: Interface ke saath bhi upcasting hoti hai! Payable p1 = new CreditCard(...) — CreditCard upcast hua Payable mein. checkout(Payable payment) method CreditCard, UPI, Cash — teeno handle karta hai. Interface upcasting = class upcasting jaise hi kaam karta hai!
PROGRAM 4 Upcasting4.java — Accessible Methods Rule
Reference Type vs Actual Object Type
public class Upcasting4 {

    static class Employee {
        String name; double salary;

        Employee(String name, double salary) {
            this.name = name; this.salary = salary;
        }

        void work() { System.out.println(name + " is working"); }

        void getSalary() {
            System.out.println(name + " salary: Rs." + salary);
        }

        void introduce() {
            System.out.println("[Employee] I am " + name);
        }
    }

    static class Developer extends Employee {
        String language; int experience;

        Developer(String name, double salary,
                   String lang, int exp) {
            super(name, salary);
            this.language = lang; this.experience = exp;
        }

        void code()  { System.out.println(name + " coding in " + language); }
        void debug() { System.out.println(name + " debugging..."); }

        void introduce() {  // Override!
            System.out.println("[Developer] I am " + name
                + ", " + experience + " yrs exp");
        }
    }

    public static void main(String[] args) {

        Developer dev = new Developer("Rahul", 80000, "Java", 3);

        System.out.println("--- Developer reference ---");
        dev.work();
        dev.getSalary();
        dev.introduce();  // Developer's version
        dev.code();       // ✅ accessible
        dev.debug();      // ✅ accessible

        System.out.println("\n--- After Upcasting ---");
        Employee emp = dev;  // UPCAST!

        emp.work();          // ✅ Employee method
        emp.getSalary();    // ✅ Employee method
        emp.introduce();    // ✅ Developer's version! (overriding)
        // emp.code();     // ❌ ERROR! Employee ref nahi jaanta code()
        // emp.debug();    // ❌ ERROR! Employee ref nahi jaanta debug()
        // emp.language;   // ❌ ERROR! Employee ref nahi jaanta language field

        System.out.println("\n--- Runtime type check ---");
        System.out.println("emp instanceof Developer? "
            + (emp instanceof Developer));  // true!
        System.out.println("emp instanceof Employee? "
            + (emp instanceof Employee));   // true!
    }
}
⚡ Output
--- Developer reference ---
Rahul is working
Rahul salary: Rs.80000.0
[Developer] I am Rahul, 3 yrs exp
Rahul coding in Java
Rahul debugging...

--- After Upcasting ---
Rahul is working
Rahul salary: Rs.80000.0
[Developer] I am Rahul, 3 yrs exp

--- Runtime type check ---
emp instanceof Developer? true
emp instanceof Employee? true
💬 Explanation: Upcasting ke baad emp.code() ERROR — Employee reference code() nahi jaanta. But emp.introduce() → Developer ka version chala — kyunki actual object Developer hai! Reference type = kaunse methods accessible. Actual object = kaunsa version runs! Ye rule hamesha yaad rakho!
🔴

Downcasting — Parent to Child

Explicit · Manual · (ChildType) parentRef — instanceof se pehle check karo!

🧠 Concept — Downcasting Explicit Kyu Hai?

Compiler nahi jaanta actual object kaunsa hai! Animal a mein Dog bhi ho sakta hai, Cat bhi — compiler ke paas yeh info nahi hoti.

Programmer ki RESPONSIBILITY hai ensure karna ki cast sahi hai. Isliye explicit cast operator lagana padta hai.

Wrong cast → ClassCastException at RUNTIME:
Animal a = new Cat();
Dog d = (Dog) a; → ❌ ClassCastException! Cat Dog nahi ban sakta!

Safe Pattern: if (a instanceof Dog) { Dog d = (Dog) a; d.bark(); }

PROGRAM 1 Downcasting1.java — Basic Downcasting
Explicit Cast + instanceof Safe Pattern
public class Downcasting1 {

    static class Animal {
        String name;
        Animal(String name) { this.name = name; }
        void eat()      { System.out.println(name + " eating"); }
        void describe() { System.out.println("Animal: " + name); }
    }

    static class Dog extends Animal {
        String breed;
        Dog(String name, String breed) {
            super(name); this.breed = breed;
        }
        void bark()  { System.out.println(name + ": Woof!"); }
        void fetch() { System.out.println(name + " fetches!"); }
        void describe() {
            System.out.println("Dog: " + name + " breed=" + breed);
        }
    }

    static class Cat extends Animal {
        boolean indoor;
        Cat(String name, boolean indoor) {
            super(name); this.indoor = indoor;
        }
        void meow() { System.out.println(name + ": Meow!"); }
        void purr()  { System.out.println(name + " purring"); }
        void describe() {
            System.out.println("Cat: " + name + " indoor=" + indoor);
        }
    }

    public static void main(String[] args) {

        // STEP 1: Upcast
        Animal a = new Dog("Tommy", "Labrador");
        a.eat();
        a.describe();     // Dog's version
        // a.bark(); ← ERROR! Not accessible

        // STEP 2: Downcast
        System.out.println("\n--- Downcast to Dog ---");
        Dog d = (Dog) a;  // explicit cast!
        d.eat();           // Animal method
        d.bark();          // ✅ NOW accessible!
        d.fetch();         // ✅ NOW accessible!

        // STEP 3: instanceof check - SAFE downcast
        System.out.println("\n--- Safe downcast with instanceof ---");
        Animal[] animals = {
            new Dog("Bruno", "German Shepherd"),
            new Cat("Whiskers", true),
            new Dog("Max", "Poodle"),
            new Cat("Luna", false)
        };

        for (Animal animal : animals) {
            animal.describe(); // polymorphism

            if (animal instanceof Dog) {
                Dog dog = (Dog) animal;  // safe downcast
                dog.bark();
            } else if (animal instanceof Cat) {
                Cat cat = (Cat) animal;  // safe downcast
                cat.meow();
            }
            System.out.println();
        }
    }
}
⚡ Output
Tommy eating
Dog: Tommy breed=Labrador

--- Downcast to Dog ---
Tommy eating
Tommy: Woof!
Tommy fetches!

--- Safe downcast with instanceof ---
Dog: Bruno breed=German Shepherd
Bruno: Woof!

Cat: Whiskers indoor=true
Whiskers: Meow!

Dog: Max breed=Poodle
Max: Woof!

Cat: Luna indoor=false
Luna: Meow!
💬 Explanation: Dog d = (Dog) a — explicit cast se Dog methods accessible ho gaye! instanceof check se safe downcast — pehle verify karo object Dog hai ya Cat, phir cast karo. Bina check ke cast karo aur object Cat ho → ClassCastException!
PROGRAM 2 Downcasting2.java — ClassCastException
Wrong Cast → Runtime Exception
public class Downcasting2 {

    static class Vehicle {
        String brand;
        Vehicle(String brand) { this.brand = brand; }
        void start() { System.out.println(brand + " start"); }
    }

    static class Car extends Vehicle {
        int doors;
        Car(String brand, int doors) {
            super(brand); this.doors = doors;
        }
        void honk() { System.out.println(brand + ": Beep!"); }
    }

    static class Truck extends Vehicle {
        double payload;
        Truck(String brand, double payload) {
            super(brand); this.payload = payload;
        }
        void loadCargo() {
            System.out.println(brand + " loading " + payload + " tons");
        }
    }

    public static void main(String[] args) {

        // SAFE downcast
        Vehicle v1 = new Car("Toyota", 4);  // upcast
        if (v1 instanceof Car) {
            Car c = (Car) v1;  // safe!
            c.honk();
            System.out.println("Doors: " + c.doors);
        }

        // UNSAFE downcast → ClassCastException!
        Vehicle v2 = new Truck("Tata", 10.0); // Truck object!

        System.out.println("Is v2 a Car?   " + (v2 instanceof Car));   // FALSE!
        System.out.println("Is v2 a Truck? " + (v2 instanceof Truck)); // TRUE!

        // Try wrong cast:
        try {
            Car wrongCast = (Car) v2; // ← RUNTIME ERROR!
            wrongCast.honk();
        } catch (ClassCastException e) {
            System.out.println("ERROR: " + e.getMessage());
            System.out.println("Truck cannot be cast to Car!");
        }

        // Correct cast:
        if (v2 instanceof Truck) {
            Truck t = (Truck) v2;  // safe!
            t.loadCargo();
        }
    }
}
⚡ Output
Toyota: Beep!
Doors: 4
Is v2 a Car?   false
Is v2 a Truck? true
ERROR: class Downcasting2$Truck cannot be cast to class Downcasting2$Car
Truck cannot be cast to Car!
Tata loading 10.0 tons
💬 Explanation: v2 actually Truck object hai — (Car) v2 try kiya → ClassCastException! Truck Car nahi ban sakta — koi IS-A relation nahi. instanceof se pehle check karo — ALWAYS use instanceof before downcast — ye golden rule hai!
PROGRAM 3 Downcasting3.java — Up + Down Together
Real-World Employee System
public class Downcasting3 {

    static class Employee {
        String name; double salary;
        Employee(String n, double s) { name = n; salary = s; }

        void work() { System.out.println(name + " working..."); }
        double calculateBonus() { return salary * 0.10; }
    }

    static class Developer extends Employee {
        String language;
        Developer(String n, double s, String lang) {
            super(n, s); this.language = lang;
        }
        void code()      { System.out.println(name + " coding " + language); }
        double calculateBonus() { return salary * 0.20; } // 20%
        void deployApp()  { System.out.println(name + " deploying app!"); }
    }

    static class Manager extends Employee {
        int teamSize;
        Manager(String n, double s, int t) {
            super(n, s); this.teamSize = t;
        }
        void conductMeeting() {
            System.out.println(name + " meeting with " + teamSize + " people");
        }
        double calculateBonus() { return salary * 0.30; } // 30%
    }

    // Upcast in parameter → Downcast inside for specific methods
    static void processEmployee(Employee emp) {
        emp.work();
        System.out.printf("Bonus: Rs.%.0f%n", emp.calculateBonus());

        if (emp instanceof Developer) {
            Developer dev = (Developer) emp;  // downcast!
            dev.code();
            dev.deployApp();
        } else if (emp instanceof Manager) {
            Manager mgr = (Manager) emp;      // downcast!
            mgr.conductMeeting();
        }
    }

    public static void main(String[] args) {

        // Upcast in array
        Employee[] team = {
            new Developer("Rahul",  80000, "Java"),
            new Manager("Priya",   120000, 8),
            new Developer("Amit",   75000, "Python"),
            new Manager("Sunita",  110000, 5)
        };

        for (Employee emp : team) {
            System.out.println("--- " + emp.name + " ---");
            processEmployee(emp);
            System.out.println();
        }

        double totalBonus = 0;
        for (Employee emp : team) totalBonus += emp.calculateBonus();
        System.out.printf("Total Bonus: Rs.%.0f%n", totalBonus);
    }
}
⚡ Output
--- Rahul ---
Rahul working...
Bonus: Rs.16000
Rahul coding Java
Rahul deploying app!

--- Priya ---
Priya working...
Bonus: Rs.36000
Priya meeting with 8 people

--- Amit ---
Amit working...
Bonus: Rs.15000
Amit coding Python
Amit deploying app!

--- Sunita ---
Sunita working...
Bonus: Rs.33000
Sunita meeting with 5 people

Total Bonus: Rs.100000
💬 Explanation: Employee[] array mein Developer aur Manager dono — upcasting! processEmployee(Employee emp) method mein instanceof + downcast se specific methods call kiye. Ek method sab handle karta hai — upcasting + downcasting ka perfect combination!
PROGRAM 4 Downcasting4.java — Animal Kingdom
Multiple Types + instanceof Chain
public class Downcasting4 {

    static class Animal {
        String name;
        Animal(String name) { this.name = name; }
        void describe() { System.out.println("Animal: " + name); }
    }

    static class Dog extends Animal {
        String breed;
        Dog(String n, String b) { super(n); breed = b; }
        void bark()  { System.out.println(name + ": Woof!"); }
        void fetch() { System.out.println(name + " fetches"); }
        void describe() { System.out.println("Dog: "+name+" ("+breed+")"); }
    }

    static class Cat extends Animal {
        boolean indoor;
        Cat(String n, boolean i) { super(n); indoor = i; }
        void meow() { System.out.println(name + ": Meow!"); }
        void purr()  { System.out.println(name + " purrs"); }
        void describe() { System.out.println("Cat: "+name+" indoor="+indoor); }
    }

    static class Bird extends Animal {
        boolean canFly;
        Bird(String n, boolean f) { super(n); canFly = f; }
        void chirp() { System.out.println(name + ": Tweet!"); }
        void fly() {
            System.out.println(name + (canFly ? " flying!" : " cannot fly!"));
        }
        void describe() { System.out.println("Bird: "+name+" canFly="+canFly); }
    }

    static void processOldWay(Animal animal) {
        animal.describe();
        if (animal instanceof Dog) {
            Dog d = (Dog) animal;
            d.bark(); d.fetch();
        } else if (animal instanceof Cat) {
            Cat c = (Cat) animal;
            c.meow();
        } else if (animal instanceof Bird) {
            Bird b = (Bird) animal;
            b.chirp(); b.fly();
        }
    }

    static void performAction(Animal animal) {
        System.out.print("Action: ");
        if      (animal instanceof Dog)  ((Dog)animal).bark();
        else if (animal instanceof Cat)  ((Cat)animal).meow();
        else if (animal instanceof Bird) ((Bird)animal).chirp();
    }

    public static void main(String[] args) {

        Animal[] animals = {
            new Dog("Tommy", "Labrador"),
            new Cat("Whiskers", true),
            new Bird("Tweety", true),
            new Bird("Penguin", false),
            new Cat("Luna", false)
        };

        System.out.println("--- Full processing ---");
        for (Animal a : animals) {
            processOldWay(a);
            System.out.println();
        }

        System.out.println("--- Quick actions ---");
        for (Animal a : animals) performAction(a);

        System.out.println("\n--- instanceof results ---");
        Animal a = new Dog("Test", "Mixed");
        System.out.println("instanceof Dog:    " + (a instanceof Dog));
        System.out.println("instanceof Cat:    " + (a instanceof Cat));
        System.out.println("instanceof Animal: " + (a instanceof Animal));
    }
}
⚡ Output
--- Full processing ---
Dog: Tommy (Labrador)
Tommy: Woof!
Tommy fetches

Cat: Whiskers indoor=true
Whiskers: Meow!

Bird: Tweety canFly=true
Tweety: Tweet!
Tweety flying!

Bird: Penguin canFly=false
Penguin: Tweet!
Penguin cannot fly!

Cat: Luna indoor=false
Luna: Meow!

--- Quick actions ---
Action: Tommy: Woof!
Action: Whiskers: Meow!
Action: Tweety: Tweet!
Action: Penguin: Tweet!
Action: Luna: Meow!

--- instanceof results ---
instanceof Dog:    true
instanceof Cat:    false
instanceof Animal: true
💬 Explanation: instanceof chain se Dog, Cat, Bird sab alag-alag handle ho rahe hain. instanceof Dog → true, instanceof Cat → false, instanceof Animal → trueDog IS-A Animal, isliye Animal bhi true hai! Inline cast ((Dog)animal).bark() — cast karke usi line pe method call!
PROGRAM 5 Downcasting5.java — Bank System Complete
Complete Real-World System — Up + Down + instanceof
public class Downcasting5 {

    static class Account {
        String accNo, holder; double balance;

        Account(String accNo, String holder, double balance) {
            this.accNo = accNo; this.holder = holder; this.balance = balance;
        }

        void deposit(double amount) {
            balance += amount;
            System.out.printf("[DEPOSIT] +Rs.%.0f → Rs.%.0f%n", amount, balance);
        }

        double getBalance() { return balance; }

        void displayInfo() {
            System.out.printf("%-12s %-15s Rs.%.0f%n", accNo, holder, balance);
        }
    }

    static class SavingsAccount extends Account {
        double interestRate;

        SavingsAccount(String accNo, String holder,
                        double balance, double rate) {
            super(accNo, holder, balance); this.interestRate = rate;
        }

        void addInterest() {
            double interest = balance * interestRate / 100;
            balance += interest;
            System.out.printf("[INTEREST @%.1f%%] +Rs.%.2f%n",
                interestRate, interest);
        }

        void withdraw(double amount) {
            if (balance - amount >= 500) {
                balance -= amount;
                System.out.printf("[WITHDRAW] -Rs.%.0f → Rs.%.0f%n", amount, balance);
            } else {
                System.out.println("Min balance Rs.500 required!");
            }
        }
    }

    static class LoanAccount extends Account {
        double loanAmount, emiAmount;
        int    remainingEmis;

        LoanAccount(String accNo, String holder,
                     double loan, double emi, int emis) {
            super(accNo, holder, 0);
            loanAmount = loan; emiAmount = emi; remainingEmis = emis;
        }

        void payEMI() {
            if (remainingEmis > 0) {
                remainingEmis--;
                System.out.printf("[EMI PAID] Rs.%.0f | Remaining: %d%n",
                    emiAmount, remainingEmis);
            } else {
                System.out.println("Loan fully paid!");
            }
        }

        double getRemainingLoan() { return emiAmount * remainingEmis; }
    }

    // Upcast in array — all stored as Account!
    static Account[] bankAccounts = {
        new SavingsAccount("SB001", "Rahul",  50000, 6.5),
        new LoanAccount("LN001",   "Priya",  500000, 15000, 36),
        new SavingsAccount("SB002", "Amit",   30000, 5.0),
        new LoanAccount("LN002",   "Sunita", 200000, 8000, 24)
    };

    static void monthlyProcess() {
        System.out.println("\n=== Monthly Processing ===");
        for (Account acc : bankAccounts) {
            System.out.println("Processing: " + acc.holder);
            // Downcast to call specific methods!
            if (acc instanceof SavingsAccount) {
                SavingsAccount sa = (SavingsAccount) acc;
                sa.addInterest();  // savings specific
            } else if (acc instanceof LoanAccount) {
                LoanAccount la = (LoanAccount) acc;
                la.payEMI();       // loan specific
            }
        }
    }

    static void displayAll() {
        System.out.println("\n=== All Accounts ===");
        System.out.printf("%-12s %-15s %-12s %-10s%n",
            "AccNo", "Holder", "Balance", "Type");
        System.out.println("-".repeat(50));
        for (Account acc : bankAccounts) {
            String type = (acc instanceof SavingsAccount) ? "SAVINGS" : "LOAN";
            System.out.printf("%-12s %-15s Rs.%-9.0f %-10s%n",
                acc.accNo, acc.holder, acc.balance, type);
            if (acc instanceof LoanAccount) {
                LoanAccount la = (LoanAccount) acc;
                System.out.printf("  └ Remaining Loan: Rs.%.0f%n", la.getRemainingLoan());
            }
        }
    }

    public static void main(String[] args) {
        displayAll();
        monthlyProcess();
        monthlyProcess();
        displayAll();
    }
}
⚡ Output
=== All Accounts ===
AccNo        Holder          Balance      Type
--------------------------------------------------
SB001        Rahul           Rs.50000     SAVINGS
LN001        Priya           Rs.0         LOAN
  └ Remaining Loan: Rs.540000
SB002        Amit            Rs.30000     SAVINGS
LN002        Sunita          Rs.0         LOAN
  └ Remaining Loan: Rs.192000

=== Monthly Processing ===
Processing: Rahul
[INTEREST @6.5%] +Rs.3250.00
Processing: Priya
[EMI PAID] Rs.15000 | Remaining: 35
Processing: Amit
[INTEREST @5.0%] +Rs.1500.00
Processing: Sunita
[EMI PAID] Rs.8000 | Remaining: 23

=== Monthly Processing ===
Processing: Rahul
[INTEREST @6.5%] +Rs.3462.50
Processing: Priya
[EMI PAID] Rs.15000 | Remaining: 34
Processing: Amit
[INTEREST @5.0%] +Rs.1575.00
Processing: Sunita
[EMI PAID] Rs.8000 | Remaining: 22

=== All Accounts ===
AccNo        Holder          Balance      Type
--------------------------------------------------
SB001        Rahul           Rs.56712     SAVINGS
LN001        Priya           Rs.0         LOAN
  └ Remaining Loan: Rs.510000
SB002        Amit            Rs.32075     SAVINGS
LN002        Sunita          Rs.0         LOAN
  └ Remaining Loan: Rs.176000
💬 Explanation: Account[] mein SavingsAccount aur LoanAccount dono — upcasting! Monthly processing mein instanceof + downcast se specific methods call: savings pe interest, loan pe EMI. Ek loop, ek method — sab kuch handle! Real-world casting ka perfect example.
📋

Complete Summary

Sab kuch ek jagah — Quick Revision
Type Direction Kaise Accessible Methods Risk
Upcasting Child → Parent ✓ Implicit (automatic) Only parent methods ✓ No risk — always safe
Downcasting Parent → Child (ChildType) ref — Explicit All child methods! ✗ ClassCastException possible
instanceof Runtime check obj instanceof Type ✓ Safe — returns true/false
Overriding + Upcast Runtime poly Automatic Child version runs! ✓ No risk

⚡ Golden Rules — Hamesha Yaad Rakho

Rule 1: Reference type → KAUNSE methods accessible hain (compile-time decision)
Rule 2: Actual object type → KAUNSA version chalega (runtime decision = polymorphism)
Rule 3: ALWAYS use instanceof before downcasting → ClassCastException se bachao!
Rule 4: Object kabhi nahi badlata — sirf reference type badlata hai!
Rule 5: Casting tabhi valid hai jab extends/implements relationship ho!

MistakeWrong ❌Sahi ✅
Downcast without check Dog d = (Dog) animal; if(animal instanceof Dog) Dog d=(Dog)animal;
Unrelated cast Dog d = (Cat) animal; extends/implements relation ZAROORI hai
Object badal gaya Think karo upcast kiya to object badla Object same rahta hai! Sirf reference badla
Missing explicit cast Dog d = animal; Dog d = (Dog) animal;
Child method via parent ref animalRef.bark(); Pehle downcast karo, phir call karo

Interview Questions

Exam / Interview mein poocha jaata hai
Q1 · Upcasting automatic kyu hai?
"Child IS-A Parent" hamesha true hota hai! Dog IS-A Animal → guaranteed! Isliye Java automatically allow karta hai — koi explicit cast operator nahi chahiye. Compiler bhi yeh relationship jaanta hai.
Q2 · Downcasting explicit kyu?
Parent reference alag-alag child objects hold kar sakta hai — Animal a = new Dog() ya Animal a = new Cat(). Compiler nahi jaanta actual type kya hai! Programmer ki responsibility hai — isliye explicit cast likhna padta hai.
Q3 · ClassCastException kab aata hai?
Jab actual object aur cast type match nahi karte:
Animal a = new Cat();
Dog d = (Dog) a;ClassCastException!
Cat Dog nahi ban sakta — koi IS-A relation nahi. Yeh RUNTIME error hai, compile time pe nahi aata!
Q4 · instanceof kya check karta hai?
ACTUAL object type check karta hai — reference type nahi!
Animal a = new Dog();
a instanceof DogTRUE (actual object Dog hai)
a instanceof AnimalTRUE (Dog IS-A Animal)
a instanceof CatFALSE (actual object Cat nahi)
Q5 · Upcasting ke baad overridden method kaunsa version chalega?
CHILD ka version! Yahi Runtime Polymorphism hai.
Animal a = new Dog();
a.sound() → Dog's sound() runs! Reference type Animal hai, but actual object Dog hai — actual object ka version chalega.
Q6 · Upcasting mein object badal jaata hai?
NAHI! Object bilkul nahi badlata — same memory address raha. Sirf reference ka type badla. dog == a → true — same heap location! Ye ek bahut common misconception hai.
Q7 · Safe downcast kaise karte hain?
Always use instanceof pattern:
if (animal instanceof Dog) {
    Dog d = (Dog) animal;
    d.bark();
}
Pehle check karo, phir cast karo — ClassCastException kabhi nahi aayega!
Java Type Casting (User-Defined) — Complete Revision Guide  |  9 Programs — Upcasting + Downcasting + instanceof