Getters and Setters in Java: What Are They Really For?

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: "Why not just make the fields public?"

\n\n

Imagine a situation: you are writing a Person class in Java. It has a name, age, and email. The simplest way is to declare the fields as public and access them directly:

\n\n
public class Person {\n    public String name;\n    public int age;\n    public String email;\n}
\n\n

"It works! — you might say. — Why do I need these getters and setters? Extra code, lines are bloated..."

\n\n

This question is one of the most popular on Stack Overflow. Thousands of developers, especially beginners, ask it. The answer you will get from an experienced Java programmer will be short: encapsulation. But behind this word lies a whole design philosophy that saves projects from chaos.

\n\n

In this article, we will not just give a definition. We will break down the real problems that getters and setters (also called accessors and mutators) solve, and demonstrate this with live code. Let's go!

\n\n

1. Encapsulation: Not Just a Buzzword

\n\n

Encapsulation is an OOP principle that states: hide the internal state of an object and provide a controlled interface for interacting with it. Getters and setters are exactly that controlled interface.

\n\n

Compare the two approaches:

\n\n

Bad Approach (public fields)

\n
Person person = new Person();\nperson.age = -5; // Negative age? Easy!\nperson.email = "not-an-email"; // Garbage data passes without validation
\n\n

Good Approach (getters and setters)

\n
public class Person {\n    private String name;\n    private int age;\n    private String email;\n\n    public int getAge() {\n        return age;\n    }\n\n    public void setAge(int age) {\n        if (age < 0 || age > 150) {\n            throw new IllegalArgumentException("Age must be between 0 and 150");\n        }\n        this.age = age;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    public void setEmail(String email) {\n        if (email == null || !email.contains("@")) {\n            throw new IllegalArgumentException("Invalid email");\n        }\n        this.email = email;\n    }\n\n    // other getters/setters...\n}
\n\n

Now, if someone tries to set the age to -5, the program will throw an exception. The data is protected. That is the power of encapsulation.

\n\n

2. Data Validation on the Fly

\n\n

Setters are the perfect place to validate input data. Without them, you would have to write validation in every place where you assign a value. That leads to code duplication and the risk of forgetting to check somewhere.

\n\n

Example with a bank account:

\n\n
public class BankAccount {\n    private double balance;\n\n    public double getBalance() {\n        return balance;\n    }\n\n    public void deposit(double amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException("Deposit amount must be positive");\n        }\n        this.balance += amount;\n    }\n\n    public void withdraw(double amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException("Withdrawal amount must be positive");\n        }\n        if (amount > balance) {\n            throw new IllegalStateException("Insufficient funds");\n        }\n        this.balance -= amount;\n    }\n}
\n\n

Notice: there is no setter for balance directly. We manage the balance through the deposit and withdraw methods, which already contain all the validation logic. The getter simply returns the current state.

\n\n

3. Flexibility: You Can Change the Implementation Without Breaking Code

\n\n

Imagine you store age in an int field, and then decide to store the date of birth and calculate the age dynamically. If you had

Blogs

Book Recommendations