Java access modifiers: which to choose when, and why

Photo by Michael Dziedzic on Unsplash
public abstract class Account implements Serializable {
    private static final long serialVersionUID 
= 4548971533036436275L;
    private final Customer accountHolderPrimary;
private Customer accountHolderSecondary = null;
private Person beneficiary = null;

private final ArrayList<Transaction> history
= new ArrayList<>();

private final Currency currency;

private CurrencyAmount balance;
    // TODO: Flag for code review
public Customer getPrimary() {
return this.accountHolderPrimary;
}

// TODO: Flag for code review
public Customer getSecondary() {
return this.accountHolderSecondary;
}

// TODO: Add parameter validation
public void setSecondary(Customer customer) {
this.accountHolderSecondary = customer;
}

// TODO: Flag for code review
public Person getBeneficiary() {
return this.beneficiary;
}

// TODO: Add parameter validation
public void setBeneficiary(Person person) {
this.beneficiary = person;
}
    public Currency getCurrency() {
return this.currency;
}
    public ArrayList<Transaction> getHistory() {
return new ArrayList<>(this.history);
}
    // TODO: Refactor to be more efficient
public CurrencyAmount getBalance() {
CurrencyAmount bal = new CurrencyAmount(0, this.currency);
for (Transaction trx : this.history) {
bal = bal.plus(trx.getAmount());
}
return bal;
}
    protected void processDeposit(Deposit deposit) {
// TODO: Implement
// Check currency (convert if needed, charge fee)
// Add deposit to history, update balance up
}

protected void processWithdrawal(Withdrawal withdrawal) {
// TODO: Implement
// if this.balance < withdrawal amount
// throw new insufficient balance exception
// Add withdrawal to history, update balance down
}
this.history.removeRange(4, 7); // ERROR: Won't compile
    public void processTransaction(Transaction transaction) {
// Check transaction date is after initial deposit date
// transaction match {
// case dep: Deposit => processDeposit(dep)
// case draw: Withdrawal => processWithdrawal(draw)
// case _ =>
this.history.add(transaction);
// }
}
    public Account(Customer customer, Deposit initialDeposit) {
this.accountHolderPrimary = customer;
this.processDeposit(initialDeposit); // WARNING!!!
this.balance = initialDeposit.getAmount();
this.currency = this.balance.getCurrency();
}
    public Account(Customer customer, Deposit initialDeposit) {
this.accountHolderPrimary = customer;
CurrencyAmount depositAmount = initialDeposit.getAmount();
assert depositAmount.getAmountInCents() > 0
: "Initial deposit amount should be positive";
this.history.add(initialDeposit);
this.balance = depositAmount;

this.currency = this.balance.getCurrency();
}
    public static final CurrencyAmount MINIMUM_BALANCE 
= CurrencyAmount.parseAmount("$100.00");
    public CurrencyAmount getBalance() {
return this.balance;
}
CurrencyAmount amount = someAccount.getBalance();
System.out.println(amount.plus(someOtherAmount));
this.balance = this.balance.plus(transaction.getAmount());
}

Considerations for interfaces

Like classes, interfaces can be public or package private. As it turns out, Java 8 allows only public access for all interface members, even if the interface itself is package private.

IntelliJ IDEA screenshot showing an unused static field.
IntelliJ IDEA screenshot showing a static field that is being used.
package java.io;public interface Serializable {}

Access modifiers for nested classes

I thought about nesting the transaction classes (Deposit, Withdrawal, etc.) into Account to illustrate nested classes. But I believe that on a real world project, I would much prefer to put the transaction classes in their own subpackage, for the sake of the single responsibility principle.

To summarize

From the Account example, and the discussion of nested classes, we can formulate the following advice for choosing access modifiers:

  • An inner class should be private if it’s not needed outside of its enclosing outer class.
  • Constants should only be public if they’re needed outside of their class’s package.
  • The getters and setters of an outer class should probably have the same access modifier as the class.
  • Helper units for a default implementation in an interface should be private (except in Java 8, in which this is not allowed).
  • If a framework allows a stricter access than public and there is no other reason for something to be public, you should use the stricter access.

Couple of notes about Scala access modifiers

There are some differences and subtleties between Java access modifiers and Scala access modifiers, which I wrote about in a separate article.

is a composer and photographer from Detroit, Michigan. He has been working on a Java program to display certain mathematical diagrams.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store