Java Inheritance
›Short versionOpen if you only have a minute.
- extends declares that the subclass is a kind of the superclass. A class extends one class. Object sits at the root.
- Callers can store a subclass instance in a superclass variable. Overridden instance methods run the subclass body.
- Constructors are not inherited. The subclass constructor calls super(...) , or the compiler inserts super().
- An override matches the signature. @Override makes a mismatch a compile error. Access can widen, not shrink.
- A different parameter list is an overload, chosen by static dispatch. It does not replace the superclass method.
- Composition is the field that holds a collaborator. Use it when the new type has the other type, and is not that type.
A savings account still has an id, a balance, a deposit, and a withdraw. It also earns interest. is the claim that a savings account is an account, so existing account code can hold one. The extra behavior lives on the .
What is inherited, and what is called
public class Account {
private int balance;
public Account(int opening) {
if (opening < 0) {
throw new IllegalArgumentException("opening");
}
balance = opening;
}
public int balance() {
return balance;
}
protected void add(int amount) {
balance += amount;
}
public void withdraw(int amount) {
if (amount <= 0 || amount > balance) {
throw new IllegalArgumentException("amount");
}
balance -= amount;
}
}balance is private. The subclass does not get to assign it. add is protected, so subclasses and the rest of the package can change the balance through a method the superclass controls. runs the superclass constructor before the subclass body, which is how the opening balance is checked once.
public class SavingsAccount extends Account {
public SavingsAccount(int opening) {
super(opening);
}
public void addInterest(int basisPoints) {
add(balance() * basisPoints / 10_000);
}
}Account account = new SavingsAccount(80) is legal.account.balance() and account.withdraw(10) compile because those methods are on Account. account.addInterest(200) does not compile through an Account variable. The compiler only sees Account. Cast, or give the variable the subclass type, when you need the extra method.
Replacing a method
An keeps the signature and supplies a new body. Calls on an Account reference still compile, and a SavingsAccount object runs the new body.
@Override
public void withdraw(int amount) {
if (amount > 1_000) {
throw new IllegalArgumentException("daily limit");
}
super.withdraw(amount);
}super.withdraw(amount) runs the Account checks after the subclass limit. Drop the super call and the subclass is responsible for the whole rule, including a non-negative balance. A method named withdraw that takes a long or a second parameter is an overload. The original withdraw(int) is still inherited, and the compiler chooses the overload from the argument types.
stops this. A final withdraw can be called by the subclass and cannot be replaced. A final Account cannot have a SavingsAccount at all. Use that when a subtype would be able to skip the balance rule.
When a field is the honest model
A monthly statement records an account. It is not an account. holds the account in a field and calls it. Code that expects an Account cannot be handed a statement, and that is the point.
public class Statement {
private final Account account;
public Statement(Account account) {
this.account = account;
}
public int closingBalance() {
return account.balance();
}
}Inheritance couples the subclass to the superclass representation and to every public and protected method. That coupling pays off when many callers should treat the subtype as the supertype. It is expensive when you only wanted to reuse a few lines. A private helper, or a field, reuses the lines without publishing a subtype.
$ javac Account.java SavingsAccount.java$ java SavingsAccount
You want to reuse Account.withdraw from a new type called Transfer. Is extends the right tool?
A transfer is not an account. It uses an account, often two of them. Give Transfer fields of type Account and call withdraw and deposit on those objects. Extending Account would let a transfer sit in any Account variable and would claim the transfer itself has a balance.