Skip to content
wannacode

Encapsulation

Problem

Minus €500

In the last lesson we put the withdrawal check in a method. But the field is open, and anyone can write to it directly:

Account account = new Account("Maria", 100);
account.withdraw(500);
account.balance = -500;
System.out.println(account.balance);
Output
-500.0

The method correctly refused to withdraw €500. The next line simply set the balance by hand. The check is there, but you can get around it. We need a way to say "only the account touches the balance".

The idea

private: only the class touches it

Put private in front of the field instead of public:

class Account {
private String owner;
private double balance;
// ...
}

Now only the code inside the Account class can reach the field. From outside, the line

account.balance = -500;

no longer compiles:

Compile error
error: balance has private access in Account

private and public are called access modifiers. They say who can see a field, a method or a constructor. The class hides its data and shows it only through its own methods. This is called encapsulation.

More precisely, if you are curious
  • Without a modifier, a field or method is visible in the same package (a group of classes in one folder). While everything is in one file, this is almost like public. So write the modifier explicitly.
  • There is also protected: visible in the class and in its subclasses. You'll meet it in the module "Inheritance and polymorphism".
  • public class Main is a public class. A file can have only one of those, and it has the same name as the file. That's why our classes above it have no public.

The idea

Reading without touching

From outside, we can't even read the balance now. So the class gives a method that only returns it:

Main.java
class Account {
private String owner;
private double balance;
public Account(String owner, double balance) {
this.owner = owner;
this.balance = balance;
}
public String getOwner() {
return owner;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
}
public class Main {
public static void main(String[] args) {
Account account = new Account("Maria", 100);
account.withdraw(30);
account.deposit(50);
System.out.println(account.getOwner() + ": €" + account.getBalance());
}
}
Output
Maria: €120.0

getBalance() is a getter: a method that returns the value of a field. Its name is get plus the field name with a capital letter. The balance changes only through deposit and withdraw, and they do the checks.

withdraw now returns boolean: whether the withdrawal went through. This way, whoever withdraws knows if it worked.

Account account = new Account("Maria", 100);
account.withdraw(500);
account.deposit(50);
System.out.println(account.getBalance());
Step by step, in words
  1. balance is private: the glass means there is no way to the field from outside.
  2. The withdrawal goes through the check in the method: 500 is more than 100. Refused, and the balance stays as it is.
  3. The deposit is checked too: 50 is a positive amount. The method adds it.
  4. getBalance() hands out a copy of the balance. The field itself stays hidden.

Try it

Refused

What will the program print?

Account account = new Account("Maria", 100);
System.out.println(account.withdraw(150));
account.deposit(-20);
System.out.println(account.getBalance());
Answer

The idea

Setter: a change with a check

Sometimes code outside needs to change a field, but not to just any value. An animal's age can change, but it can't be negative:

class Animal {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 0) {
this.age = age;
}
}
}
Animal cat = new Animal();
cat.setAge(4);
cat.setAge(-3);
System.out.println(cat.getAge());
Output
4

setAge is a setter: a method that stores a value in a field after it checks it. With -3, the check fails and the age stays 4.

The idea

Which field gets a setter?

Not every field needs a setter, and some don't even need a getter. Think about what code outside should be able to do with the product:

  • the name and the price are set when the product is created and are only read after that: getter only;
  • the quantity in the cart changes, but it's at least 1: a getter and a setter with a check.
class Product {
private String name;
private double price;
private int quantity;
public Product(String name, double price) {
this.name = name;
this.price = price;
this.quantity = 1;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
if (quantity >= 1) {
this.quantity = quantity;
}
}
}
Product coffee = new Product("Coffee", 7.50);
coffee.setQuantity(3);
coffee.setQuantity(0);
System.out.println(coffee.getName() + " x" + coffee.getQuantity());
Output
Coffee x3

The rule is: start with private for everything and open only what you need. A setter that stores anything without a check brings back the problem from the first card.

In Java, you write getters and setters by hand. IntelliJ IDEA can generate them: Alt+Insert → Getter and Setter.

Try it

What's missing?

The balance should be visible only inside the class. What goes in the blank?
class Account {
     double balance;
}

Try it

Find the bug

The field is private, and yet the balance becomes -500:

Account account = new Account();
account.setBalance(100);
account.withdraw(30);
account.setBalance(-500);
System.out.println(account.getBalance());
Output
-500.0

Which line in the class breaks the protection?

Pick the line with the bug.

Exercise

Hidden data

Solve the tasks in your editor. All fields are private.

1. Person. Person with a name and an age, a constructor, the getters getName() and getAge() and introduce().

Output
Maria is 34 years old.
Hi, I'm Maria and I'm 34 years old.

2. An account with no minus. Account with an owner and a balance. A deposit accepts only a positive amount. A withdrawal returns whether it went through and never lets the balance go below 0. Code outside can read the balance, but can't write it.

Main
Account account = new Account("Peter", 50);
account.deposit(25);
account.deposit(-10);
System.out.println(account.withdraw(100));
System.out.println(account.withdraw(40));
System.out.println("Balance: €" + account.getBalance());
Output
false
true
Balance: €35.0

3. Animal. Animal with a species and an age. The species is set in the constructor and is only read after that. The age has a setter that refuses a negative number and prints a message. The constructor goes through the same check too.

Main
Animal cat = new Animal("cat", 3);
cat.setAge(4);
cat.setAge(-1);
System.out.println(cat.getSpecies() + ", " + cat.getAge() + " years old");
Output
Invalid age: -1
cat, 4 years old

More tasks

  • Grade. A Grade class with a subject name and a grade from 2 to 6. The setter refuses anything outside those limits. Try it with 5, 7 and 1.
  • Product with a discount. Add a method to the Product from the lesson that applies a discount in percent and accepts only 1 to 90%. The price has no setter.

Test

Check yourself

1. The balance field is private. Where can it be changed from?
2. Why does the account have no setter for the balance?
3. The code calls setAge(-3), and setAge checks age >= 0. What happens to the age?
4. What does encapsulation mean?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·