Quiz 2

OOP Concepts — Abstraction, Encapsulation, Inheritance, Polymorphism

2837 words
14 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# OOP Concepts — Abstraction, Encapsulation, Inheritance, Polymorphism ## 🎯 Learning Objectives By the end of this topic, you will be able to: - Explain the four pillars of OOP with concrete analogies - Distinguish between abstraction and encapsulation - Describe how subtyping enables polymorphism - Understand how...

OOP Concepts — Abstraction, Encapsulation, Inheritance, Polymorphism

🎯 Learning Objectives

By the end of this topic, you will be able to:
  • Explain the four pillars of OOP with concrete analogies
  • Distinguish between abstraction and encapsulation
  • Describe how subtyping enables polymorphism
  • Understand how dynamic dispatch works at runtime
  • Compare Java's OOP implementation with Python's

📋 Prerequisites


1. Intuition: Why Object-Oriented Programming?

1.1 What Problem Does OOP Solve?

Imagine building a house. You could:
  1. Assembly-line approach (procedural): Have a single huge list of instructions: "Cut board A, nail to board B, attach door C, paint wall D..."
  2. Modular approach (OOP): Build rooms as separate modules. Each room has its own door, windows, and walls. You can reuse the "Bedroom" design in multiple houses, customize it, and fix a leaky window without touching the rest of the house. OOP organizes code around objects (things in the real world) rather than actions. This makes large programs easier to understand, maintain, and extend.

1.2 The Four Pillars (Mental Model)

Think of a restaurant kitchen:
OOP PillarKitchen AnalogyMeaning in Code
AbstractionThe menu lists dishes, not recipesHide complex implementation; expose simple interface
EncapsulationIngredients are in labeled containers, not scatteredBundle data + methods; protect data from outside access
Inheritance"Chef" is a specialized "Employee"Create new classes based on existing ones
PolymorphismAny chef can cook, but each has their specialtySame method name, different behavior by type

2. Abstraction — Hiding Complexity

2.1 Intuition

You drive a car using the steering wheel and pedals (interface). You don't need to understand how the engine, transmission, and fuel injection work. The complexity is hidden — that's abstraction.

2.2 In Code

Abstraction means exposing only the essential details and hiding the internal implementation:
java
// WHAT it does (accessible interface)
public class CoffeeMachine {
    public Coffee brew(EspressoOrder order) { ... }
    public Coffee brew(LatteOrder order) { ... }
    // HOW it does it (hidden from users)
    private void heatWater() { ... }
    private void grindBeans() { ... }
    private void pressurize() { ... }
}
Users of CoffeeMachine call brew() and get coffee. They don't need to know about water heating or bean grinding.

2.3 Java Mechanisms for Abstraction

  • Abstract classes (abstract class): Partially implemented classes that cannot be instantiated
  • Interfaces (interface): Fully abstract contracts that classes implement
  • Methods: Public methods define the API; private methods are implementation details

3. Encapsulation — Data Protection

3.1 Intuition

A bank account has a balance that only the account holder can change through specific operations (deposit, withdraw). You can't just walk in and scribble a new number on the teller's screen. The balance is encapsulated — protected from direct access.

3.2 In Code

Encapsulation bundles data (fields) with the methods that operate on that data, and restricts direct access:
java
public class BankAccount {
    private double balance;  // Private — cannot access from outside
    public BankAccount(double initialBalance) {
        if (initialBalance >= 0) {
            this.balance = initialBalance;
        }
    }
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    public boolean withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
    public double getBalance() {  // Controlled access via getter
        return balance;
    }
}

3.3 The private Keyword

private fields can only be accessed within the same class. This prevents:
  • Setting an invalid value (e.g., negative balance)
  • Dependencies on internal implementation (you can change the internal structure without affecting external code)

3.4 Getters and Setters

The standard Java pattern for controlled access:
java
public class Person {
    private String name;
    private int age;
    // Getter
    public String getName() {
        return name;
    }
    // Setter with validation
    public void setAge(int age) {
        if (age >= 0 && age <= 150) {
            this.age = age;
        }
    }
}

4. Inheritance — "Is-a" Relationship

4.1 Intuition

A Dog is a Mammal. A Mammal is an Animal. Each level inherits properties from the level above. A dog breathes (like all animals) and has hair (like all mammals), but also barks (specific to dogs).

4.2 In Code

java
// Base class (parent)
public class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat() {
        System.out.println(name + " is eating.");
    }
    public void sleep() {
        System.out.println(name + " is sleeping.");
    }
}
// Derived class (child) — Dog IS-A Animal
public class Dog extends Animal {
    public Dog(String name) {
        super(name);  // Call parent constructor
    }
    public void bark() {
        System.out.println(name + " says Woof!");
    }
    @Override
    public void eat() {  // Override parent's method
        System.out.println(name + " is eating dog food.");
    }
}

4.3 Key Concepts

extends keyword: Indicates that a class inherits from another:
java
public class Dog extends Animal { ... }
super keyword: References the parent class:
java
super(name);              // Call parent constructor
super.eat();              // Call parent method
@Override annotation: Documents that a method replaces a parent method. The compiler will warn if you mis-type the method name.

4.4 Constructor Chaining

When you create a Dog, Java calls ALL constructors up the chain:
java
Dog d = new Dog("Buddy");
// 1. Animal("Buddy") runs first (super class)
// 2. Dog("Buddy") runs next (subclass)

4.5 Method Overriding

A subclass can provide its own implementation of a parent method. The @Override annotation tells the compiler:
java
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println(name + " is eating cat food.");
    }
}

4.6 final Keyword on Classes and Methods

  • final class: Cannot be extended (e.g., String, Integer)
  • final method: Cannot be overridden
java
public final class FinalClass { }  // Cannot be inherited
public class Parent {
    public final void cannotOverride() { }
}

5. Polymorphism — Many Forms

5.1 Intuition

"Press the brake pedal" — in any car, pressing the brake slows the car. But the internal mechanism differs: disc brakes, drum brakes, regenerative brakes (electric cars). The interface is the same (press pedal), but the implementation differs (polymorphism).

5.2 Compile-Time Polymorphism (Method Overloading)

Same method name, different parameters — resolved at compile time:
java
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    public double add(double a, double b) {
        return a + b;
    }
    public int add(int a, int b, int c) {
        return a + b + c;
    }
}
The compiler chooses the right method based on argument types and count.

5.3 Runtime Polymorphism (Dynamic Dispatch)

The JVM decides which method to call at runtime based on the actual object type, not the reference type:
java
Animal myPet = new Dog("Buddy");  // Reference is Animal, actual is Dog
myPet.eat();  // Calls Dog's eat() — dynamic dispatch!
(Diagram)

5.4 Upcasting and Downcasting

Upcasting (safe, implicit):
java
Dog dog = new Dog("Buddy");
Animal animal = dog;  // Upcast — Dog → Animal (always safe)
Downcasting (risky, explicit):
java
Animal animal = new Dog("Buddy");
Dog dog = (Dog) animal;      // OK — animal IS a Dog
Cat cat = (Cat) animal;      // Compiles but ClassCastException at runtime!
Use instanceof to check before downcasting:
java
if (animal instanceof Dog) {
    Dog dog = (Dog) animal;  // Safe
}

5.5 Polymorphism Enables Flexibility

java
public class Zoo {
    public void feedAll(Animal[] animals) {
        for (Animal a : animals) {
            a.eat();  // Each animal eats its own way
        }
    }
}
// Usage
Animal[] animals = {new Dog("Buddy"), new Cat("Kitty"), new Dog("Max")};
Zoo zoo = new Zoo();
zoo.feedAll(animals);
// Buddy is eating dog food.
// Kitty is eating cat food.
// Max is eating dog food.

6. Understanding static Members

6.1 Instance vs Static

Instance (non-static)Static
Belongs toEach object separatelyThe class itself
MemoryHeap (per object)Method area (one copy)
Accessed viaObject reference (obj.field)Class name (ClassName.field)
Can access otherInstance and static membersOnly static members directly
java
public class Student {
    public String name;           // Instance variable
    public static int count;      // Class variable (one shared value)
    public static final String COLLEGE = "IIT Madras";  // Constant
    public Student(String name) {
        this.name = name;
        count++;  // Increment shared counter
    }
}
// Usage
Student s1 = new Student("Alice");
Student s2 = new Student("Bob");
System.out.println(Student.count);  // 2 — shared across instances
System.out.println(s1.COLLEGE);     // IIT Madras

7. Java vs Python: OOP Comparison

FeatureJavaPython
Everything is an object?Primitives are notYes (even ints are objects)
Multiple inheritanceNot supported for classes (interfaces only)Supported (with MRO)
Access modifiersprivate, protected, public, package-private_ (convention), __ (name mangling)
OverloadingYes (compile-time)Not directly (default args, *args)
OverridingExplicit @OverrideAutomatic (duck typing)
Dynamic dispatchVirtual methods by defaultAll methods are virtual
Abstract classesabstract class keywordABC from abc module
Interfacesinterface keywordInformal (duck typing)
this/selfthis (implicit)self (explicit first parameter)
ConstructorSame name as class__init__
Destructorfinalize() (deprecated)__del__

8. Common Pitfalls

Pitfall 1: Confusing Overloading with Overriding

java
public class Parent {
    public void doSomething(int x) { }
}
public class Child extends Parent {
    public void doSomething(double x) { }  // OVERLOADING, not overriding!
}
Why: Different parameter type → overloading. The child has two methods: the inherited doSomething(int) and a new doSomething(double). Fix: Use @Override annotation — it will tell you if you're actually overriding or accidentally overloading.

Pitfall 2: Accessing Private Members in Subclass

java
public class Parent {
    private int secret = 42;
}
public class Child extends Parent {
    public void show() {
        System.out.println(secret);  // Compile error!
    }
}
Why: private members are not inherited. They exist in the object but are only accessible within the declaring class. Fix: Use protected if the subclass should access it.

Pitfall 3: instanceof with null

java
Dog dog = null;
if (dog instanceof Dog) {  // Returns false, not NullPointerException!
    System.out.println("It's a dog");
}
Why: instanceof elegantly handles null — if the reference is null, it returns false. This is actually useful for safe checks.

Pitfall 4: Static Methods Cannot Be Overridden

java
public class Parent {
    public static void greet() {
        System.out.println("Parent");
    }
}
public class Child extends Parent {
    public static void greet() {  // This HIDES, not overrides
        System.out.println("Child");
    }
}
// Usage
Parent p = new Child();
p.greet();  // Prints "Parent", not "Child"!
Why: Static methods are resolved at compile-time based on reference type, not runtime object type. This is called method hiding, not overriding.

Pitfall 5: Forgetting super() Call

java
public class Parent {
    public Parent(int x) {
        System.out.println("Parent: " + x);
    }
}
public class Child extends Parent {
    public Child() {
        // Implicit super() — but Parent has no no-arg constructor!
    }
}
Why: If the parent class has no no-arg constructor, the child must explicitly call super(value) as the first statement in its constructor. Fix: Add super(0); as the first statement in Child().

9. Practice Questions

Q1: What is the output?
java
class A {
    public void show() {
        System.out.println("A");
    }
}
class B extends A {
    public void show() {
        System.out.println("B");
    }
}
public class Test {
    public static void main(String[] args) {
        A obj = new B();
        obj.show();
    }
}
Answer: B
Dynamic dispatch: Even though the reference type is A, the actual object is B. Java determines at runtime which show() to call based on the actual object type (B). Q2: What is wrong with this code?
java
public class Main {
    public static void main(String[] args) {
        Animal a = new Animal("Generic");
    }
}
abstract class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public abstract void sound();
}
Answer: Animal is abstract, so you cannot instantiate it with new Animal(...). Abstract classes can only be extended by concrete subclasses. Fix: Create a concrete subclass or remove abstract from the class declaration. Q3: Explain the output
java
class Parent {
    int x = 10;
}
class Child extends Parent {
    int x = 20;
}
public class Test {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.x);

        Child c = new Child();
        System.out.println(c.x);
    }
}
Answer: 10 then 20
Fields are not polymorphic! Java resolves field access at compile-time based on reference type, not runtime type. p.x uses Parent.x = 10 because p is declared as Parent. c.x uses Child.x = 20 because c is declared as Child. This is called field shadowing, not overriding. Q4: What is encapsulation and why is it important?
Answer: Encapsulation is the bundling of data (fields) with methods that operate on that data, while restricting direct access to the internal state. It's important because:
  1. Protection: Prevents invalid state (e.g., negative age, empty name)
  2. Flexibility: Internal implementation can change without affecting external code
  3. Maintainability: Dependencies are clearly defined through the public API
  4. Testing: Components can be tested in isolation
In Java, encapsulation is achieved through private fields with public getter/setter methods. Q5: What is the difference between abstraction and encapsulation?
Answer:
AbstractionEncapsulation
WhatHiding complexity, showing essential featuresBundling data + methods, protecting data
GoalReduce complexity by hiding detailsPrevent unauthorized access
HowAbstract classes, interfacesprivate fields, getters/setters
FocusWhat an object doesHow an object's state is managed
AnalogyCar steering wheel (interface)Car engine casing (protection)
Q6: What is the output? Explain the concept.
java
class MathUtils {
    public static int square(int x) {
        return x * x;
    }
}
public class Test {
    public static void main(String[] args) {
        int result = MathUtils.square(5);
        System.out.println(result);
    }
}
Answer: 25
The square method is static, meaning it belongs to the class, not to any instance. It is called using the class name (MathUtils.square(5)) without creating an object. Static methods can only access other static members directly. Q7: Why does this code throw ClassCastException?
java
Animal a = new Dog();
Cat c = (Cat) a;
Answer: The Animal reference a actually points to a Dog object. Downcasting it to Cat asks the JVM to treat a Dog as a Cat, which is impossible. instanceof check would prevent this:
java
if (a instanceof Cat) {
    Cat c = (Cat) a;  // Safe
} else {
    System.out.println("Not a cat");
}
Q8: Which OOP pillar does method overriding implement?
Answer: Polymorphism — specifically runtime polymorphism (dynamic dispatch). Overriding allows a subclass to provide its own implementation of a method defined in the parent class. When called through a parent reference, the JVM determines at runtime which version to execute based on the actual object type.
Related: Method overloading implements compile-time polymorphism (also called static polymorphism or ad-hoc polymorphism). Q9: What does super do in a constructor?
Answer: super() calls the parent class constructor. It must be the first statement in a constructor. If you don't explicitly write super(), the compiler inserts an implicit super() call to the no-arg constructor. If the parent class has no no-arg constructor, you must explicitly call super(args).
java
public Child(int x) {
    super(x);  // Must be first statement
    // Other initialization...
}
Q10: Can you override a private method? A static method? A final method?
Answer:
  • Private method: No. Private methods are not visible to subclasses, so they cannot be overridden (they can be redeclared, but that's a new method, not an override).
  • Static method: No. Static methods are hidden, not overridden. The method called depends on the reference type, not the object type.
  • Final method: No. final explicitly prevents overriding.

📐 Key Concepts

ConceptDescriptionJava Keyword
AbstractionHide complexity, expose essentialsabstract, interface
EncapsulationProtect data, bundle fields + methodsprivate, getters/setters
InheritanceCreate "is-a" hierarchiesextends
PolymorphismSame interface, different implementations@Override, virtual dispatch
Dynamic dispatchRuntime method resolution based on object typeImplicit (all non-final, non-static methods)
Static bindingCompile-time method resolutionstatic, final, private
UpcastingTreating subclass as superclass (safe)Implicit
DowncastingTreating superclass as subclass (risky)Explicit (Type) + instanceof

🔗 Cross-References

Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.