Session 19: More about inheritance

The Object class (Section 11.5)
    The equals() method
    The toString() method
Protection levels (Section 11.2)

The Object class

Textbook: Section 11.5

Every class defined in Java is automatically a subclass of the Object class (located in the java.lang package). If you leave out the extends clause, Java will insert one in for you, indicating that you are extending the Object class.

As a result, every class we've defined has been inheriting from the Object class, and so every object we declare has all the methods declared by the Object class.

Two of these methods are particularly useful to know about. Because they're in the Object class, they're methods that every object has.

The equals() method

We've seen the equals() method for the String class. But the method is actually defined in the Object class, so that every object has it.

The equals() method defined in the Object class takes an Object as a parameter and returns true if the object's memory address is identical to the parameter object's memory address.

Account a = new Account();
Account b = new Account();
Account c = a;
IO.println(a.equals(b)); // prints false
IO.println(a.equals(c)); // prints true

Often, methods need to override the equals() method. The String class overrides this method, for example.

The toString() method

The toString() method is for creating a string representation of an object's value. By default, this simply includes the object's class name and a useless memory address. This particular method is frequently overridden.

It's particularly useful to override this method when you might want to print an object. For example, there's an IO.print() method that takes an object as a parameter. This method calls toString() on the object to determine exactly what to print.

Protection levels

Textbook: Section 11.2

We've been throwing around the public and private keywords, but we really haven't discussed them explicitly yet. Today we get to cover them.

Java actually has four levels of protection. These levels apply to both methods and variables (whether class members or instance members).

Frequently, it's a good idea to make instance variables protected instead of private. We might have done this with our balance instance variable in the Account class, for example.

public class Account {
    protected double balance;
    public Account() { balance = 0.0; }
    public double getBalance() { return balance; }
    public void deposit(double amt) { balance += amt; }
    public void withdraw(double amt) { balance += amt; }
}
If we did this, then the instance variable would be accessible within subclasses.
public class SavingsAccount extends Account {
    protected double interest;
    public SavingsAccount(double int) { interest = int; }
    public void addInterest() { balance += interest * balance; }
}
(This solves the problem we saw yesterday of how a class might override the deposit() method, making it so that addInterest() doesn't necessarily work as we intended when we wrote it.) But we're still forbidden from using the variable in other classes.
public class Main {
    public static void main(String[] args) {
        Account mine = new Account();
        IO.println(mine.balance); // ERROR: accessing protected member
    }
}