Java Arena
Step 5 of 8

private, protected and public in a subclass

A subclass object holds everything its superclass has, private variables too. But the subclass's own code may use only what the superclass lets it use. This step is about those rules.

private stays private

The bank from step 3 wants its savings accounts to earn interest. A first try at addInterest changes the balance directly:

Main.java
public class Main {
    public static void main(String[] args) {
        SavingsAccount savings = new SavingsAccount("Mia", 500, 3);
        savings.addInterest();
    }
}
BankAccount.java
public class BankAccount {
    private String owner;
    private int balance;

    public BankAccount(String owner, int balance) {
        this.owner = owner;
        this.balance = balance;
    }

    public int getBalance() {
        return this.balance;
    }

    public void deposit(int amount) {
        if (amount > 0) {
            this.balance += amount;
        }
    }
}
SavingsAccount.java
public class SavingsAccount extends BankAccount {
    private int interestPercent;

    public SavingsAccount(String owner, int balance, int interestPercent) {
        super(owner, balance);
        this.interestPercent = interestPercent;
    }

    public void addInterest() {
        int interest = this.getBalance() * this.interestPercent / 100;
        this.balance += interest;
    }
}
What javac prints
SavingsAccount.java:11: error: balance has private access in BankAccount
        this.balance += interest;
            ^
1 error

Every SavingsAccount object has a balance: it's in the BankAccount part of the object. But private means that only BankAccount's own code may use the variable, and SavingsAccount's code isn't BankAccount's code. The same goes for private methods: a subclass can't call them.

Use the superclass's public methods

Usually the best fix is to use what BankAccount offers everyone. getBalance() gives the balance, and deposit(...) adds to it:

public void addInterest() {
    int interest = this.getBalance() * this.interestPercent / 100;
    this.deposit(interest);
}

This way BankAccount stays the only class that changes the balance directly, and its rules still hold: deposit ignores an amount of 0 or less, so interest can't sneak in a negative amount.

protected

Sometimes the public methods aren't enough. A checking account may go below zero, down to its overdraft limit. In the program below, BankAccount has a withdraw that refuses to take more than the balance, and none of its public methods can make a balance negative. So BankAccount marks the balance protected: its subclasses may use it directly.

Main.java
public class Main {
    public static void main(String[] args) {
        BankAccount plain = new BankAccount("Leo", 100);
        plain.withdraw(150);
        System.out.println(plain.getBalance());

        CheckingAccount checking = new CheckingAccount("Ada", 100, 200);
        checking.withdraw(150);
        System.out.println(checking.getBalance());

        SavingsAccount savings = new SavingsAccount("Mia", 500, 3);
        savings.addInterest();
        System.out.println(savings.getBalance());
    }
}
BankAccount.java
public class BankAccount {
    private String owner;
    protected int balance;

    public BankAccount(String owner, int balance) {
        this.owner = owner;
        this.balance = balance;
    }

    public int getBalance() {
        return this.balance;
    }

    public void deposit(int amount) {
        if (amount > 0) {
            this.balance += amount;
        }
    }

    public void withdraw(int amount) {
        if (amount > 0 && amount <= this.balance) {
            this.balance -= amount;
        }
    }
}
CheckingAccount.java
public class CheckingAccount extends BankAccount {
    private int overdraftLimit;

    public CheckingAccount(String owner, int balance, int overdraftLimit) {
        super(owner, balance);
        this.overdraftLimit = overdraftLimit;
    }

    @Override
    public void withdraw(int amount) {
        if (amount > 0 && amount <= this.balance + this.overdraftLimit) {
            this.balance -= amount;
        }
    }
}
SavingsAccount.java
public class SavingsAccount extends BankAccount {
    private int interestPercent;

    public SavingsAccount(String owner, int balance, int interestPercent) {
        super(owner, balance);
        this.interestPercent = interestPercent;
    }

    public void addInterest() {
        int interest = this.getBalance() * this.interestPercent / 100;
        this.deposit(interest);
    }
}
Output
100
-50
515
  • The plain account refuses to pay out 150 from 100. CheckingAccount overrides withdraw with its own rule, and changes this.balance directly, which protected allows.
  • SavingsAccount still uses the public methods. protected allows direct use; it doesn't require it.
  • protected also lets in every class in the same package. A package is a group of classes that belong together, such as Java's own java.util. The programs in this course have no package line, so all their classes are in one package, the default one, and there even Main could use balance directly. So in these programs, protected is more a note to other programmers ("subclasses may use this") than a lock.

The four levels

Written before a variable or method Who can use it
private only the class itself
nothing classes in the same package
protected classes in the same package, and subclasses
public every class

A good habit: keep instance variables private, and let subclasses work through methods. Make a variable protected only when the subclasses really need it, as CheckingAccount did.

One more rule: an overriding method can't be less visible than the method it overrides. Object's toString is public, so a toString that is private, protected or has nothing written before it doesn't compile: javac says attempting to assign weaker access privileges; was public.

Your task

Challenge 1 of 3

The bank's bonus account adds 5 dollars to every deposit of 100 or more. BonusAccount's deposit doesn't compile, since the balance is private in BankAccount. Rewrite it with BankAccount's public methods, and don't change BankAccount.java: call BankAccount's own deposit with super.deposit(...), once for the amount and once more for a bonus. A deposit of 0 or less adds nothing, and gets no bonus. The check runs:

BonusAccount account = new BonusAccount("Ada", 50);
account.deposit(120);
System.out.println(account.getBalance());
account.deposit(40);
System.out.println(account.getBalance());
account.deposit(-100);
System.out.println(account.getBalance());

and expects:

175
215
215

Challenge 2

A museum's exhibits have a condition from 0 to 100, which drops as they age. Only a restorer may raise it again, so Exhibit has no public method for that. Paintings can be restored. In Exhibit.java, make condition protected (not public). Then, in Painting.java, write the method restore(), which raises the condition by 30, but never above 100. The check runs:

Painting dawn = new Painting("Harbor at Dawn", 50);
dawn.restore();
System.out.println(dawn);
dawn.restore();
System.out.println(dawn);
dawn.age(15);
System.out.println(dawn.getCondition());

and expects:

Harbor at Dawn, condition 80
Harbor at Dawn, condition 100
85

Challenge 3

Here are the bank's classes from the lesson: BankAccount with its balance protected, and CheckingAccount, which overrides withdraw. What does this program print?