Java Encapsulation

Java Encapsulation

Encapsulation, one of the four fundamental OOP concepts, is the mechanism of wrapping the data (variables) and code acting on the data (methods) together as a single unit.

The main purpose of encapsulation is to ensure data hiding. To achieve this, you must:

  1. Declare the variables of a class as private.
  2. Provide public "setter" and "getter" methods to modify and view the variables' values.

Why Encapsulation?


Getters and Setters

To access the private variables, we use public "get" and "set" methods.

By convention, getters start with get and setters start with set, followed by the variable name (e.g., getName(), setName()).

Encapsulation with Getters and Setters

public class Person {
  private String name; // private = restricted access

// Getter public String getName() { return name; }

// Setter public void setName(String newName) { // We can add validation logic here if (newName != null && !newName.isEmpty()) { this.name = newName; } } }

public class Main { public static void main(String[] args) { Person myObj = new Person(); // myObj.name = "John"; // This would cause an error myObj.setName("John"); // Set the value of the name variable to "John" System.out.println(myObj.getName()); } }

In the setName method, we added a simple validation check. This is a key benefit of encapsulation: you control how your data is set, preventing invalid data from being assigned to your object's attributes.


Deep Dive: The Power of Setters (Data Validation)

The true power of Encapsulation isn't just hiding data—it is protecting it.

🏦 The "Bank Account" Analogy: Think of your bank account. You do not have direct access to the bank's internal database variable that holds your balance (that would be dangerous, as you could just change it to a million dollars!). Instead, you have to interact with the bank teller or an ATM (the methods). The ATM enforces rules: it checks your PIN and ensures you have enough funds before allowing a withdrawal, protecting the internal data from invalid changes.

By making your variables private, you force other developers to use your public setters, where you can enforce business rules to prevent bad data!

Protecting State with Validation

public class Main {
  static class Employee {
    private int age;
    public void setAge(int age) {
      // Rule: Age cannot be negative or over 150
      if (age > 0 && age < 150) {
        this.age = age;
        System.out.println("Age successfully set to: " + this.age);
      } else {
        System.out.println("Error: Invalid age provided! Age not updated.");
      }
    }
    public int getAge() {
      return this.age;
    }
  }

public static void main(String[] args) { Employee emp = new Employee(); emp.setAge(-5); // Will print an error and protect the internal variable! emp.setAge(30); // Will succeed } }


Read-Only and Write-Only Classes

Encapsulation allows you to granularly control access, making a class (or a specific variable) read-only or write-only.

Read-Only Example

public class Main {
  static class Student {
    // Initialized once, maybe in a constructor
    private String studentID = "STU-9923"; 
    // Only a Getter is provided! No Setter!
    public String getStudentID() {
      return studentID;
    }
  }
  public static void main(String[] args) {
    Student myStudent = new Student();
    System.out.println("Student ID: " + myStudent.getStudentID());
    // myStudent.studentID = "STU-0000"; // Error: Cannot access private variable
    // myStudent.setStudentID("STU-0000"); // Error: Method does not exist
  }
}

Constructor Encapsulation (Validating Initial State)

While setters protect your data from bad updates, what prevents someone from creating an object with bad data right from the start using new?

You should apply the same encapsulation rules to your constructors! A good practice is to have your constructor call your setter methods, or contain the same validation logic, ensuring the object starts in a valid state.

Validating in Constructor

public class Main {
  static class Product {
    private double price;
    // Constructor uses validation
    public Product(double initialPrice) {
      if (initialPrice > 0) {
        this.price = initialPrice;
      } else {
        System.out.println("Invalid price! Defaulting to 1.0");
        this.price = 1.0;
      }
    }
    public double getPrice() { return this.price; }
  }
  public static void main(String[] args) {
    Product badProduct = new Product(-10.50);
    System.out.println("Final Price: $" + badProduct.getPrice());
  }
}

Deep Dive: Defensive Copying (Protecting Mutable Objects)

There is a common, hidden pitfall in encapsulation: returning mutable objects (objects that can be changed, like an ArrayList or a Date).

If your private variable is an ArrayList, and your public getter simply returns that ArrayList, external code can add or remove items from your list without ever calling a setter! This completely breaks encapsulation.

🛡️ The Solution: Return a Defensive Copy. Instead of handing out the original object, hand out a clone of the object. If external code modifies the clone, your internal data remains safe!

Defensive Copying Example

import java.util.ArrayList;
import java.util.List;
public class Main {
  static class SecureVault {
    private List<String> secretCodes = new ArrayList<>();
    public SecureVault() {
      secretCodes.add("ALPHA-123");
    }
    // BAD: Returns the original list. Anyone can modify it!
    // public List<String> getSecretCodes() { return secretCodes; }
    // GOOD: Returns a new copy of the list.
    public List<String> getSecretCodes() {
      return new ArrayList<>(secretCodes);
    }
  }
  public static void main(String[] args) {
    SecureVault vault = new SecureVault();
    List<String> stolenCodes = vault.getSecretCodes();
    // The thief tries to add a fake code to the vault
    stolenCodes.add("FAKE-CODE"); 
    // But the vault is safe because they only modified their copy!
    System.out.println("Vault contains FAKE-CODE? " + vault.getSecretCodes().contains("FAKE-CODE"));
  }
}

Advanced Notes: Strong Encapsulation


Exercise

?

What are the two main things needed to achieve encapsulation?