Weeks
JavaWeek 3 · Inheritance, Polymorphism and Modifiers
Week 3 Notes

Java,
Week 3

Programming Concepts using Java, Week 3: object oriented design, subclasses and inheritance, dynamic dispatch and polymorphism, the Java class hierarchy and Object, subtyping versus inheritance, and the modifiers public, private, static and final. Every Java example in these notes was really compiled and run.

8 chapters20 worked examples~56 min read
OO designextendsPolymorphismequals()Subtypingstatic / finalInterview prep

Chapters

  1. 01The Philosophy of OO ProgrammingLecture 1
  2. 02Subclasses and InheritanceLecture 2
  3. 03Dynamic Dispatch and PolymorphismLecture 3
  4. 04The Java Class HierarchyLecture 4
  5. 05Subtyping versus InheritanceLecture 5
  6. 06Java ModifiersLecture 6
  7. 07Interview Question BankInterview
  8. 08Week 3 Summary SheetAll lectures
01
Lecture 1

The Philosophy of OO Programming

Why object oriented design puts data first, how to find objects in a problem, what to decide when designing them, and how classes relate.

1.1Algorithms + Data Structures = Programs

This is the title of Niklaus Wirth's introduction to Pascal. Traditionally, algorithms come first and the data representation comes later: you write procedures, and they share whatever data is lying around.

Object oriented designReverses the traditional focus. First identify the data we want to maintain and manipulate. Then identify the algorithms that operate on that data.
Traditional: algorithms firstObject oriented: data firstglobal dataproc 1proc 2proc 3proc 4proc 5proc 62000 procedures share one pool of data.A bug can be in any of them.class 1own data~20 methodsclass 2own data~20 methodsclass 3own data~20 methodsclass 4own data~20 methods100 classes, each with about 20 methods.Debug one object: search 20, not 2000.Algorithms + Data Structures = Programs (Wirth)
Figure 1. Procedures sharing global data versus classes owning their data
  • The claim: this works better for large systems.
  • Example, a simple web browser: 2000 procedures maintaining global data, versus 100 classes each with about 20 methods. The second is much easier to grasp.
  • Debugging: if an object is in an incorrect state, you search among 20 methods rather than 2000 procedures.
Link to Week 1. This is the "data-centric view" of objects, and the reason data refinement is less painful when the data and its methods live in one class.

1.2An example: an order processing system

An order processing system typically involves these things:

Objects (nouns)What happens to them (verbs)
Itemsare added to orders
Ordersare shipped, cancelled
Shipping addressesare attached to orders
Paymentsare accepted, rejected
Accountsare checked for credit
The rule of thumb
  • Nouns signify objects, verbs denote the methods that operate on objects. So we give each Order a method to add an item.

1.3Designing objects: behaviour, state, identity

AspectQuestion it answersIn Java
BehaviourWhat methods do we need to operate on the objects?the public methods
StateHow does the object react when methods are invoked? State is the information in the instance variables.the fields
EncapsulationState should not change unless a method operates on it.private
IdentityHow do we distinguish between different objects of the same class?the object itself (its reference)

Identity is separate from state. Two orders may contain the same item and so have the same state, yet they are two different objects. And the features interact: state affects behaviour. You cannot add an item to an order that has been shipped, and you cannot ship an empty order.

Item.java
public class Item {
    private String name;
    private double price;

    public Item(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() { return name; }
    public double getPrice() { return price; }
}
Account.java
public class Account {
    private double credit;

    public Account(double credit) {
        this.credit = credit;
    }

    public boolean creditOK(double amount) {      // Order depends on Account for this
        return amount <= credit;
    }
}
Order.java
import java.util.ArrayList;

public class Order {
    private ArrayList<Item> items = new ArrayList<>();    // aggregation: an Order contains Items
    private boolean shipped = false;
    private boolean cancelled = false;

    public boolean addItem(Item i) {
        if (shipped || cancelled) {
            return false;                  // state affects behaviour
        }
        items.add(i);
        return true;
    }

    public double total() {
        double t = 0;
        for (Item i : items) {
            t += i.getPrice();
        }
        return t;
    }

    public boolean ship(Account a) {       // dependence: Order needs Account
        if (items.isEmpty() || shipped || cancelled) {
            return false;                  // cannot ship an empty order
        }
        if (!a.creditOK(total())) {
            return false;
        }
        shipped = true;
        return true;
    }

    public boolean cancel() {
        if (shipped) {
            return false;
        }
        cancelled = true;
        return true;
    }
}

ArrayList is a growable list from java.util (arrays have a fixed size); we use it here without fuss and meet it properly later. Now watch state, behaviour and identity at work:

Main.java
public class Main {
    public static void main(String[] args) {
        Account acct = new Account(1000);
        Item pen = new Item("pen", 10);
        Order o1 = new Order();
        Order o2 = new Order();

        System.out.println("ship empty order:     " + o1.ship(acct));
        o1.addItem(pen);
        o2.addItem(pen);
        System.out.println("o1 == o2 (identity):  " + (o1 == o2));
        System.out.println("same total (state):   " + (o1.total() == o2.total()));
        System.out.println("ship o1:              " + o1.ship(acct));
        System.out.println("add item after ship:  " + o1.addItem(pen));
        System.out.println("cancel after ship:    " + o1.cancel());
        System.out.println("cancel o2:            " + o2.cancel());
    }
}
Output
ship empty order:     false
o1 == o2 (identity):  false
same total (state):   true
ship o1:              true
add item after ship:  false
cancel after ship:    false
cancel o2:            true

1.4Relationships between classes

Orderitems, statusaddItem(i)ship()cancel()Itemname, priceAccountcreditOK()ExpressOrdershippingCharge()priority()containsneedsis aItem does not dependon Account: low couplingnouns = classesverbs = methodsdashed arrow = dependence diamond = aggregation hollow triangle = inheritance
Figure 2. Dependence, aggregation and inheritance in the order system
RelationshipMeaningExample
DependenceOne class needs another to do its job. Robust design minimises dependencies, also called coupling, between classes.Order needs Account to check credit; Item does not depend on Account
AggregationOne object contains other objects.Order contains Item objects
InheritanceOne object is a specialised version of another.ExpressOrder inherits from Order, adding shipping charges and priority handling
ExpressOrder.java
public class ExpressOrder extends Order {      // inheritance: a specialised Order
    public double shippingCharge() {
        return 50.0 + 0.1 * total();           // extra behaviour, reuses total() from Order
    }
}
Main.java
public class Main {
    public static void main(String[] args) {
        ExpressOrder x = new ExpressOrder();
        x.addItem(new Item("book", 200));       // inherited from Order
        x.addItem(new Item("pen", 10));
        System.out.println(x.total());           // inherited
        System.out.println(x.shippingCharge());  // new in ExpressOrder
        Order o = x;                             // an ExpressOrder can be used as an Order
        System.out.println(o.addItem(new Item("bag", 90)));
    }
}
Output
210.0
71.0
true
Summary of the lecture. An object-oriented approach can help organise code in large projects: identify the nouns, give them the verbs, keep their state private, and keep the connections between classes few and clear.

1.5Exam tips and common mistakes

Exam tips
  • Compare in one line: traditional = algorithms first, then data; OO = data first, then algorithms.
  • Learn the four design aspects: behaviour, state, encapsulation, identity, and the three relationships: dependence, aggregation, inheritance.
  • "Same state" does not mean "same object": identity is separate.
Common mistakes
  • Mixing up aggregation (has-a, contains) with inheritance (is-a).
  • Thinking dependence is bad per se. The aim is to minimise it, not to remove it.
  • Designing with public fields so that anyone can change an object's state behind its back.

1.6Interview corner: what they may ask

Likely questionChanceCrisp answer
What is the difference between procedural and object-oriented design?Very likelyProcedural starts from algorithms (procedures sharing data); OO starts from the data, grouped with the methods that operate on it into classes.
What is the difference between state, behaviour and identity?Very likelyState: the values in the instance variables. Behaviour: what the methods do. Identity: which object it is; two objects can have equal state and still be different objects.
Dependence vs aggregation vs inheritance?Very likelyDependence: uses another class. Aggregation: contains objects of another class (has-a). Inheritance: is a specialised version of another class (is-a).
What is coupling, and why do we want it low?LikelyCoupling is how much classes depend on each other. Low coupling means a change in one class rarely forces changes in others.
How do you find the classes and methods when designing?LikelyRead the requirements: nouns are candidate classes, verbs are candidate methods.
Next · Lecture 2Subclasses and Inheritance
02
Lecture 2

Subclasses and Inheritance

Extending a class, what a subclass inherits, why it cannot see private data, constructing the parent part with super, and using a subclass in place of its parent.

2.1A typical Java class: Employee

Employee.java
public class Employee {
    private String name;
    private double salary;

    // Constructors
    public Employee(String n, double s) {
        name = n;
        salary = s;
    }

    public Employee(String n) {
        this(n, 500.00);
    }

    // Mutator methods
    public boolean setName(String s) {
        name = s;
        return true;
    }

    public boolean setSalary(double x) {
        if (x < 0) {
            return false;
        }
        salary = x;
        return true;
    }

    // Accessor methods
    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    // Other methods
    public double bonus(float percent) {
        return (percent / 100.0) * salary;
    }
}
  • An Employee class with two private instance variables, name and salary.
  • Some constructors to set up the object, and accessor and mutator methods for the instance variables.
  • A public method, bonus, that computes a bonus.
Two slips in the lecture's sketch (extra). It writes public String getSalary(), but a salary is a double, so the return type must be double. And the mutators return boolean: that is the lecture's way of reporting whether the update was accepted (here setSalary refuses a negative salary).
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Employee("Asha", 1000);
        System.out.println(e.getName() + " earns " + e.getSalary());
        System.out.println(e.setSalary(-5));          // refused
        System.out.println(e.setSalary(1200));        // accepted
        System.out.println(e.getSalary());
        System.out.println(e.bonus(5));               // 5 percent of 1200
        Employee n = new Employee("Ravi");           // one-argument constructor: default salary
        System.out.println(n.getSalary());
    }
}
Output
Asha earns 1000.0
false
true
1200.0
60.0
500.0

2.2Subclasses: Manager extends Employee

Managers are special types of employees with extra features. A manager has a secretary, for example. Instead of copying Employee, we write a subclass that extends it:

Manager.java
public class Manager extends Employee {
    private String secretary;

    public Manager(String n, double s, String sn) {
        super(n, s);
        secretary = sn;
    }

    public boolean setSecretary(String s) {
        secretary = s;
        return true;
    }

    public String getSecretary() {
        return secretary;
    }
}
class Employeeprivate name, salarygetName(), setSalary(..)bonus(float percent)class Manager extends Employee+ private secretary+ setSecretary(..)+ bonus(..) overriddeninheritsEmployee e = new Manager(...);estatic type: EmployeeManager objectname, salarysecretaryruntime type: Managere.setSecretary("C")compile errorthe compiler checks the STATIC typee.bonus(5)allowedruns Manager.bonus (RUNTIME type decides)(dynamic dispatch)
Figure 3. Manager inherits from Employee; the variable e has a static type and an object with a runtime type
  • A Manager object inherits the other fields and methods from Employee. Every manager has a name, a salary, and the methods to access and manipulate them.
  • We say Manager is a subclass of Employee, and Employee is the parent (or super) class.
  • In general a subclass has more features than its parent: it can add instance variables and methods (and, in the next lecture, override methods).
Main.java
public class Main {
    public static void main(String[] args) {
        Manager m = new Manager("Ravi", 2000, "Meena");
        System.out.println(m.getName());              // inherited from Employee
        System.out.println(m.getSalary());            // inherited
        System.out.println(m.getSecretary());         // defined in Manager
        m.setSalary(2500);                            // inherited mutator
        System.out.println(m.getSalary());
    }
}
Output
Ravi
2000.0
Meena
2500.0
PythonJava
Declare a subclassclass Manager(Employee):class Manager extends Employee
Call the parent constructorsuper().__init__(n, s)super(n, s);
Parentsseveral allowedexactly one (next lecture)

2.3Private data and the constructor: super

A Manager object does not automatically get access to the private data of its parent class. This matters because it is common to extend a parent class written by someone else, whose private details you must not need to know:

Manager.java
public class Manager extends Employee {
    public Manager(String n, double s) {
        super(n, s);
    }

    public String peek() {
        return name;            // name is private to Employee
    }
}
Error
Manager.java:7: error: name has private access in Employee
        return name;            // name is private to Employee
               ^
1 error

So how can the constructor of Manager set the instance variables that are private to Employee? It uses the parent class's constructor, through super:

Employee.java + Manager.java
public class Employee {
    ...
    public Employee(String n, double s) {
        name = n;
        salary = s;
    }
    public Employee(String n) {
        this(n, 500.00);
    }
}

public class Manager extends Employee {
    ...
    public Manager(String n, double s, String sn) {
        super(n, s);            // hand the name and salary to Employee
        secretary = sn;
    }
}
  • super(...) must be the first statement of the subclass constructor. The parent part of the object is built first.
  • If you do not write it, Java inserts an implicit super(). That fails to compile when the parent has no no-argument constructor:
Main.java
public class Main {
    public static void main(String[] args) {
        Manager m = new Manager("A");
    }
}
Error
Manager.java:2: error: constructor Employee in class Employee cannot be applied to given types;
    public Manager(String n) {
                             ^
  required: String,double
  found:    no arguments
  reason: actual and formal argument lists differ in length
1 error
Source slip (extra). In the next lecture's sketch the class header is written public class Manager {, without extends Employee. Without extends there is no inheritance at all.

2.4Inheritance rules: what can stand in for what

  • Every Manager is an Employee, but not every Employee is a Manager.
  • So we can use a subclass in place of a superclass: Employee e = new Manager(...);. This is the subtyping of Week 1, Lecture 5.
  • But the other direction will not work: Manager m = new Employee(...);.
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Manager("Ravi", 2000, "Meena");     // fine
        System.out.println(e.getName());
    }
}
Output
Ravi
Main.java
public class Main {
    public static void main(String[] args) {
        Manager m = new Employee("Asha", 1000);
    }
}
Error
Main.java:3: error: incompatible types: Employee cannot be converted to Manager
        Manager m = new Employee("Asha", 1000);
                    ^
1 error
Summary of the lecture
  • A subclass extends a parent class and inherits its instance variables and methods.
  • It can add more instance variables and methods, and can also override methods (next lecture).
  • Subclasses cannot see private components of the parent. Use super to reach the parent's constructor.

2.5Exam tips and common mistakes

Exam tips
  • extends = inherit. The subclass inherits everything non-private and can add more, but cannot see the parent's private members.
  • Constructors are not inherited: a subclass writes its own and calls super(...) first.
  • Direction of assignment: Parent p = new Child(); compiles; Child c = new Parent(); does not.
Common mistakes
  • Forgetting extends, or forgetting that the subclass needs to call the parent constructor.
  • Trying to use name directly in the subclass: it is private, use getName().
  • Putting super(...) after another statement.
  • Assuming constructors are inherited, so a subclass has the same constructors as its parent.

2.6Interview corner: what they may ask

Likely questionChanceCrisp answer
What does extends do? What does a subclass inherit?Very likelyIt makes the class a subclass: it inherits the parent's non-private fields and methods and can add its own. Private members are inherited as part of the object but are not directly accessible.
Are constructors inherited?Very likelyNo. Each class defines its own; a subclass constructor starts by calling a parent constructor with super(...), explicitly or implicitly.
What does super mean?Very likelyA reference to the parent part of the current object: super(...) calls a parent constructor; super.method() calls the parent's version of a method.
Why can't a subclass use the parent's private fields?Very likelyEncapsulation: the parent may change its representation, and a subclass written by someone else must not break. It uses the parent's public or protected methods instead.
Can a child object be assigned to a parent variable, and the reverse?Very likelyChild to parent: yes, it is automatic (every Manager is an Employee). Parent to child variable: no, not without a cast, and the cast fails at run time unless the object really is a child.
Next · Lecture 3Dynamic Dispatch and Polymorphism
03
Lecture 3

Dynamic Dispatch and Polymorphism

Overriding methods, static type versus run-time dispatch, polymorphism, signatures and overloading, and type casting.

3.1Overriding a method

  • A subclass inherits instance variables and methods from its parent, cannot see the parent's private parts, can add more, and can also override methods.
  • A Manager can redefine bonus(), using the parent's version through super:
Manager.java
public double bonus(float percent) {
    return 1.5 * super.bonus(percent);
}
  • It uses the parent class's bonus() via super and overrides the definition in the parent class: a manager gets 1.5 times the normal bonus.
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Employee("Asha", 1000);
        Manager m = new Manager("Ravi", 2000, "Meena");
        System.out.println(e.bonus(5));      // percent / 100 * salary
        System.out.println(m.bonus(5));      // 1.5 * (5 / 100 * 2000)
    }
}
Output
50.0
150.0

Two things the compiler checks about an override

The lecture's fragment omits public. An overriding method may not be less visible than the method it replaces:

Main.java
public class Main {
    public static void main(String[] args) {
        Manager m = new Manager("A", 1, "B");
    }
}
Error
Manager.java:18: error: bonus(float) in Manager cannot override bonus(float) in Employee
    double bonus(float percent) {      // overrides Employee.bonus
           ^
  attempting to assign weaker access privileges; was public
1 error

And the annotation @Override (an extra, but a habit worth having) makes the compiler confirm that you really are overriding. A typo in the name or the parameter list would silently create a new method otherwise:

Main.java
public class Main {
    public static void main(String[] args) {
        Manager m = new Manager("A", 1, "B");
    }
}
Error
Manager.java:18: error: method does not override or implement a method from a supertype
    @Override
    ^
1 error

3.2Static type versus dynamic dispatch

Consider Employee e = new Manager(...);. Two questions:

QuestionWho answersAnswer
Can we invoke e.setSecretary(...)?Static type checking: e is declared an Employee, so it can only refer to methods in Employee.No: compile error
Which bonus() does e.bonus(p) use?Static would say Employee.bonus(); dynamic says Manager.bonus(), the real object's version.Java uses the dynamic one
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Manager("Ravi", 2000, "Meena");
        e.setSecretary("C");
    }
}
Error
Main.java:4: error: cannot find symbol
        e.setSecretary("C");
         ^
  symbol:   method setSecretary(String)
  location: variable e of type Employee
1 error
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Manager("Ravi", 2000, "Meena");
        System.out.println(e.bonus(5));      // Manager's version runs
    }
}
Output
150.0
  • Dynamic dispatch (also called dynamic binding, late method binding) turns out to be far more useful, and it is the default in Java. In C++ it is optional: you must mark a method virtual.
for (...) System.out.println(emparray[i].bonus(5.0f));emparray[0]Employee salary = 1000bonus = percent/100 * salary50.0[1]Manager salary = 2000bonus = 1.5 * super.bonus(5)150.0The caller never asks "which kind?": each object knows how to compute its bonus.
Figure 4. The same call, each object runs its own bonus()

3.3Polymorphism

An array of Employee can hold both kinds of employee. Every one of them “knows” how to calculate its bonus correctly:

Main.java
public class Main {
    public static void main(String[] args) {
        Employee[] emparray = new Employee[2];
        Employee e = new Employee("Asha", 1000);
        Manager m = new Manager("Ravi", 2000, "Meena");

        emparray[0] = e;
        emparray[1] = m;

        for (int i = 0; i < emparray.length; ++i) {
            System.out.println(emparray[i].bonus(5.0f));
        }
    }
}
Output
50.0
150.0
Source slip (extra). The lecture calls bonus(5.0), but the parameter is a float and 5.0 is a double, so it does not compile. Write 5.0f (Week 2: a double will not squeeze into a float without a cast).
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Employee("A", 1000);
        System.out.println(e.bonus(5.0));
    }
}
Error
Main.java:4: error: incompatible types: possible lossy conversion from double to float
        System.out.println(e.bonus(5.0));
                                   ^
1 error
  • Recall the event simulation loop that motivated Simula to introduce objects (Week 1, Lecture 5): one generic call, many behaviours.
  • This is called runtime polymorphism, or inheritance polymorphism.

3.4Signatures and overloading

  • The signature of a function is its name and the list of argument types. We can have different functions with the same name and different signatures, for example multiple constructors.
  • Example: the Java class Arrays has a method sort that sorts arrays of any scalar type. It works because Arrays defines overloaded versions: sort(double[] a), sort(int[] a), and so on.
Main.java
public class Main {
    public static void main(String[] args) {
        double[] darr = {3.5, 1.25, 2.0};
        int[] iarr = {5, 3, 9, 1};
        java.util.Arrays.sort(darr);            // calls sort(double[])
        java.util.Arrays.sort(iarr);            // calls sort(int[])
        System.out.println(java.util.Arrays.toString(darr));
        System.out.println(java.util.Arrays.toString(iarr));
    }
}
Output
[1.25, 2.0, 3.5]
[1, 3, 5, 9]
OverloadingOverriding + dynamic dispatch
Methodssame name, different signatures, in one classsame name, same signature, in parent and subclass
Choice madeat compile time (static), from the argument typesat run time (dynamic), from the object's real class
ExampleArrays.sort(int[]) / Arrays.sort(double[])Employee.bonus() / Manager.bonus()
A wording slip in the lecture (extra). The slide first lists "Overriding: ... choice is static" and the next one corrects it with "Dynamic dispatch: multiple methods, same signature, choice made at run-time". The correct statement is the second: overriding is resolved at run time.
Main.java
public class Main {
    static void describe(Employee x) { System.out.println("describe(Employee)"); }
    static void describe(Manager x)  { System.out.println("describe(Manager)"); }

    public static void main(String[] args) {
        Employee e = new Manager("Ravi", 2000, "Meena");
        describe(e);                // overloads: picked from the STATIC type Employee
        System.out.println(e.bonus(5));     // override: picked from the RUNTIME type Manager
    }
}
Output
describe(Employee)
150.0

3.5Type casting

  • With Employee e = new Manager(...), can we get e.getSecretary() to work? Static type checking disallows this.
  • Type casting converts e to Manager: ((Manager) e).setSecretary(s).
  • The cast fails (an error at run time) if e is not a Manager. You can test first with instanceof.
Employee e = ...;eManager object((Manager) e).setSecretary(s)workseplain Employee object((Manager) e)ClassCastExceptionat RUN timeif (e instanceof Manager) { ((Manager) e).setSecretary(s); }Test first with instanceof, then cast: the cast cannot fail.Casting a number, (int) 29.98, is different: it converts (gives 29).
Figure 5. A cast is checked at run time; instanceof makes it safe
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Manager("Ravi", 2000, "Meena");
        ((Manager) e).setSecretary("Sam");                   // legal: e really is a Manager
        System.out.println(((Manager) e).getSecretary());

        Employee f = new Employee("Asha", 1000);
        if (f instanceof Manager) {
            System.out.println("a manager");
        } else {
            System.out.println("not a manager");             // the test protects us
        }
        Manager bad = (Manager) f;                           // no test: fails at run time
    }
}
Error
Sam
not a manager
Exception in thread "main" java.lang.ClassCastException: class Employee cannot be cast to class Manager (Employee and Manager are in unnamed module of loader 'app')
	at Main.main(Main.java:13)

Casting also works for the basic data types, where it converts the value (Week 2):

Main.java
public class Main {
    public static void main(String[] args) {
        double d = 29.98;
        int nd = (int) d;
        System.out.println(nd);
    }
}
Output
29
Pattern matching for instanceof (extra, Java 16+). if (f instanceof Manager mgr) { mgr.setSecretary("Sam"); } tests and casts in one step, so you cannot cast wrongly.
Summary of the lecture
  • A subclass can override a method from its parent.
  • Dynamic dispatch calls the most appropriate method, based on the run-time identity of the object. It differs from overloading.
  • Later we will see another kind, structural polymorphism: one sorting function for an array of any type that supports comparison. Java calls it generics.
  • Use type casting (and reflection) to overcome static type restrictions.

3.6Exam tips and common mistakes

Exam tips
  • Two checks happen: the compiler uses the declared (static) type to decide what may be called; the JVM uses the real (runtime) type to decide which version runs.
  • Overloading = same name, different parameters, static. Overriding = same name, same parameters, dynamic.
  • A cast never changes the object; it only changes how the compiler lets you see it, and it is verified at run time.
Common mistakes
  • Expecting e.setSecretary() to work because the object is a Manager: the compiler looks at the variable's type.
  • Changing the parameter type in the subclass, which creates an overload instead of an override. Use @Override.
  • Making the overriding method less visible (dropping public).
  • Casting without checking with instanceof: ClassCastException.

3.7Interview corner: what they may ask

Likely questionChanceCrisp answer
What is polymorphism? Runtime vs compile-time?Very likelyOne call, many behaviours. Runtime polymorphism is overriding with dynamic dispatch, chosen by the object's real class. Compile-time polymorphism is overloading, chosen by the argument types.
Overloading vs overriding?Very likelyOverloading: same name, different parameter lists, resolved at compile time. Overriding: a subclass redefines an inherited method with the same signature, resolved at run time.
What is dynamic dispatch?Very likelyThe JVM picks the method implementation at run time from the object's actual class, not from the variable's declared type. It is the default in Java.
What does @Override do?LikelyIt asks the compiler to check that the method really overrides a parent method, catching typos in names or parameter types.
What is a ClassCastException? How do you avoid it?Very likelyA bad cast discovered at run time: the object is not an instance of the target class. Test with instanceof first, or avoid the cast by using polymorphism.
Can an overriding method have a different return type or weaker access?SometimesAccess cannot be weaker (compile error). The return type must be the same, or a subtype of the original (covariant return).
Next · Lecture 4The Java Class Hierarchy
04
Lecture 4

The Java Class Hierarchy

Why Java has no multiple inheritance, the tree rooted at Object, toString(), equals() versus ==, and overriding with the right signature.

4.1Multiple inheritance

Can a subclass extend several parent classes? Suppose C1 and C2 both define public int f(); and C3 extends both. If C3 does not override f(), which one do we use?

C1: int f()C2: int f()C3 extends C1, C2c3.f() ?Which f() runs if C3 does not override it? There is no good answer,so Java does NOT allow it: a class extends exactly one class.Interfaces can be combined: C3 implements I1, I2 and must itself say which f() to use.
Figure 6. Two parents with the same method: which f() does C3 get?
  • Java does NOT allow multiple inheritance of classes.
  • C++ allows it, but only if C1 and C2 have no conflict.
Main.java
public class Main {
    public static void main(String[] args) {
        C3 c = new C3();
    }
}
Error
C3.java:1: error: '{' expected
public class C3 extends C1, C2 {
                          ^
1 error

Interfaces: several parents without the conflict (extra)

A class can implement many interfaces. If two of them supply a default f(), Java still refuses to guess: the class must say which one it wants.

Main.java
public class Main {
    interface I1 { default int f() { return 1; } }
    interface I2 { default int f() { return 2; } }

    static class C3 implements I1, I2 {
    }

    public static void main(String[] args) { }
}
Error
Main.java:5: error: types I1 and I2 are incompatible;
    static class C3 implements I1, I2 {
           ^
  class C3 inherits unrelated defaults for f() from types I1 and I2
1 error
Main.java
public class Main {
    interface I1 { default int f() { return 1; } }
    interface I2 { default int f() { return 2; } }

    static class C3 implements I1, I2 {
        public int f() {
            return I1.super.f() + I2.super.f();     // the class decides
        }
    }

    public static void main(String[] args) {
        System.out.println(new C3().f());
    }
}
Output
3

4.2The Java class hierarchy and Object

  • No multiple inheritance, so the hierarchy is tree-like.
  • In fact there is a universal superclass Object. A class with no extends clause silently extends it.
  • Useful methods defined in Object, inherited by every class we write: public boolean equals(Object o) (defaults to pointer equality) and public String toString().
Objectequals() toString() hashCode()StringDateEmployeeint[]ScannerManagerevery class has exactly one parentA class with no extends clause silently extends Object.
Figure 7. Every class sits in one tree below Object
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Manager("Ravi", 2000, "Meena");
        System.out.println(e.getClass().getName());
        System.out.println(e instanceof Employee);
        System.out.println(e instanceof Object);
        System.out.println(Manager.class.getSuperclass().getName());
        System.out.println(Employee.class.getSuperclass().getName());
        System.out.println(Object.class.getSuperclass());
    }
}
Output
Manager
true
true
Employee
java.lang.Object
null

4.3toString() and printing an object

  • toString() converts an object to a String. The default one is not helpful: it gives the class name and a hash code.
  • To print an object o, use System.out.println(o + ""). Concatenating with a string implicitly invokes o.toString(). (println(o) does the same.)
  • Override toString() to describe the state; dynamic dispatch makes the print use your version.
Main.java
public class Main {
    public static void main(String[] args) {
        Date d = new Date(13, 8, 2015);
        System.out.println(d);                 // println(o) calls d.toString()
        System.out.println(d + "");            // so does concatenation
        System.out.println("due: " + d);

        Object plain = new Object();
        String s = plain.toString();           // the default: class name @ hash code
        System.out.println(s.startsWith("java.lang.Object@"));
    }
}
Output
13/8/2015
13/8/2015
due: 13/8/2015
true

4.4equals() versus ==

Exploit the tree to write generic functions. A function that takes Object[] works for an array of any class. The lecture's example is a search:

Main.java
public class Main {
    public static int find(Object[] objarr, Object o) {
        int i;
        for (i = 0; i < objarr.length(); ++i) {
            if (objarr[i] == o) {
                return i;
            }
        }
        return -1;
    }
}
Error
Main.java:4: error: cannot find symbol
        for (i = 0; i < objarr.length(); ++i) {
                              ^
  symbol:   method length()
  location: variable objarr of type Object[]
1 error
Two slips in the lecture's find (extra). (1) An array's size is the field objarr.length, with no brackets; length() does not compile (Week 2). (2) == on objects is always pointer equality. The lecture says that "x == y invokes x.equals(y)" and that an overridden equals would be picked by objarr[i] == o. That is not true in Java: == never calls equals. To use the redefined equals, call it: objarr[i].equals(o).
class Dateequals(Object o)inherited, ==equals(Date d)YOUR overload@Override would reject itDate d1, d2; Object o2 = d2;d1.equals(d2)static type Date -> equals(Date): trued1.equals(o2)static type Object -> equals(Object): falseThe overload is chosen from the STATIC types of the arguments,so code that only knows "Object" (collections, find()) never sees it.Fix: write public boolean equals(Object o) and add @Override.Then also override hashCode() so equal objects have equal hash codes.
Figure 8. Why an equals(Date) overload is invisible to Object-typed code
Date.java
public class Date {
    private int day, month, year;

    public Date(int d, int m, int y) {
        day = d;
        month = m;
        year = y;
    }

    @Override
    public boolean equals(Object d) {
        if (d instanceof Date) {
            Date myd = (Date) d;
            return this.day == myd.day && this.month == myd.month && this.year == myd.year;
        }
        return false;
    }

    @Override
    public int hashCode() {                  // equal dates must have equal hash codes
        return java.util.Objects.hash(day, month, year);
    }

    @Override
    public String toString() {
        return day + "/" + month + "/" + year;
    }
}
Main.java
public class Main {
    static int find(Object[] objarr, Object o) {
        for (int i = 0; i < objarr.length; ++i) {
            if (objarr[i].equals(o)) {
                return i;
            }
        }
        return -1;
    }

    static int findSame(Object[] objarr, Object o) {
        for (int i = 0; i < objarr.length; ++i) {
            if (objarr[i] == o) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        Object[] dates = { new Date(9, 9, 2009), new Date(1, 2, 2000) };
        Date target = new Date(1, 2, 2000);          // equal in state, a different object

        System.out.println(find(dates, target));      // equals(): compares state
        System.out.println(findSame(dates, target));  // ==: compares identity
        System.out.println(target.equals(dates[1]));
        System.out.println(target == dates[1]);
    }
}
Output
1
-1
true
false

4.5Overriding equals() correctly

For a class Date with instance variables day, month and year, we may wish to override equals() to compare the object's state. The obvious attempt:

Date.java
public boolean equals(Date d) {
    return this.day == d.day && this.month == d.month && this.year == d.year;
}

Unfortunately boolean equals(Date d) does not override boolean equals(Object o). The parameter types differ, so it is a new overload sitting beside the inherited method. Code that only knows Object (a collection, the find above) never sees it:

Main.java
public class Main {
    static int find(Object[] objarr, Object o) {
        for (int i = 0; i < objarr.length; ++i) {
            if (objarr[i].equals(o)) {
                return i;
            }
        }
        return -1;
    }

    static int findSame(Object[] objarr, Object o) {
        for (int i = 0; i < objarr.length; ++i) {
            if (objarr[i] == o) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        Date d1 = new Date(1, 2, 2000);
        Date d2 = new Date(1, 2, 2000);
        Object o2 = d2;
        System.out.println(d1.equals(d2));       // static type Date: equals(Date), compares state
        System.out.println(d1.equals(o2));       // static type Object: equals(Object), the inherited ==
        System.out.println(find(new Object[] { d1 }, d2));
    }
}
Output
true
false
-1

@Override exposes the mistake immediately, and the fix is to take an Object, test with instanceof, cast, and compare. The lecture's correct version is the one shown earlier:

Date.java
public class Date {
    private int day, month, year;

    @Override
    public boolean equals(Date d) {
        return this.day == d.day && this.month == d.month && this.year == d.year;
    }
}
Error
Date.java:4: error: method does not override or implement a method from a supertype
    @Override
    ^
1 error

equals and hashCode go together (extra)

If two objects are equal, they must have the same hash code, or hash-based collections such as HashSet will treat them as different. That is why the correct Date above also overrides hashCode().

Main.java
public class Main {
    static class Bad {
        int v;
        Bad(int v) { this.v = v; }
        @Override public boolean equals(Object o) { return o instanceof Bad && ((Bad) o).v == v; }
    }

    public static void main(String[] args) {
        java.util.Set<Date> good = new java.util.HashSet<>();
        good.add(new Date(1, 2, 2000));
        good.add(new Date(1, 2, 2000));
        System.out.println(good.size());          // equals + hashCode agree: one date

        java.util.Set<Bad> bad = new java.util.HashSet<>();
        bad.add(new Bad(1));
        bad.add(new Bad(1));
        System.out.println(bad.size());           // equals but no hashCode: two
    }
}
Output
1
2

4.6Overriding looks for the "closest" match

Suppose Employee defines public boolean equals(Employee e) and Manager defines no equals. Then consider two managers and the call m1.equals(m2). Which method is used? equals(Employee e) and equals(Object o) both accept a Manager; Java picks the most specific, closest one: equals(Employee e).

Main.java
public class Main {
    static class Employee {
        public boolean equals(Employee e) {
            System.out.println("equals(Employee) chosen");
            return true;
        }
    }

    static class Manager extends Employee { }

    public static void main(String[] args) {
        Manager m1 = new Manager();
        Manager m2 = new Manager();
        m1.equals(m2);                 // Employee is closer than Object
        Object o = m2;
        System.out.println(m1.equals(o));   // static type Object: only equals(Object) fits
    }
}
Output
equals(Employee) chosen
false
Summary of the lecture
  • Java does not allow multiple inheritance; a subclass extends exactly one parent, so the hierarchy is a tree rooted at Object.
  • Object supplies defaults such as equals() and toString(), implicitly inherited by every class.
  • When we override, check the signature carefully.

4.7Exam tips and common mistakes

Exam tips
  • == on objects = same object. equals() = same state, if the class overrides it. Default equals is just ==.
  • The signature to override is exactly public boolean equals(Object o); write @Override.
  • A class has one parent but may implement many interfaces.
Common mistakes
  • Writing equals(Date d) instead of equals(Object o): an overload, not an override.
  • Using == and expecting it to call equals.
  • Overriding equals but not hashCode.
  • Forgetting that every class already has toString() and equals() from Object.

4.8Interview corner: what they may ask

Likely questionChanceCrisp answer
== vs equals() for objects?Very likely== compares references (same object). equals() compares whatever the class defines, usually state; by default it is the same as ==.
How do you override equals correctly? What else must you override?Very likelyTake an Object parameter, check instanceof, cast, compare the fields. Also override hashCode so equal objects have equal hash codes.
Why doesn't Java support multiple inheritance of classes?Very likelyTo avoid the diamond problem: two parents supplying the same method with no good rule for choosing. Interfaces give the useful part, several subtypes, without inheriting conflicting state.
What is Object?Very likelyThe root of the class hierarchy. Every class extends it, directly or indirectly, so every object has equals, hashCode, toString and getClass.
What does the default toString() print? Why override it?LikelyThe class name, @ and a hash code, which is unhelpful. Override it to describe the state; println and string concatenation use it automatically.
Why does HashSet need hashCode to agree with equals?LikelyIt first uses the hash code to pick a bucket, then equals inside the bucket. Equal objects with different hash codes land in different buckets and are never compared.
Next · Lecture 5Subtyping versus Inheritance
05
Lecture 5

Subtyping versus Inheritance

Why one class hierarchy gives two different things, the deque, stack and queue example in real Java, and how composition re-uses code without subtyping.

5.1Two jobs done by one class hierarchy

The class hierarchy provides both subtyping and inheritance. They are different ideas that happen to share one mechanism, so it pays to keep them apart.

Subtyping
  • The capabilities of the subtype are a superset of those of the main type.
  • If B is a subtype of A, wherever an object of type A is needed, an object of type B can be used.
  • Employee e = new Manager(...); is legal.
Inheritance
  • The subtype can re-use the code of the main type.
  • B inherits from A if some functions for B are written in terms of functions of A.
  • Manager.bonus() uses Employee.bonus().
Main.java
public class Main {
    public static void main(String[] args) {
        Employee e = new Manager("Ravi", 2000, "Meena");   // subtyping: a Manager used as an Employee
        System.out.println(e.bonus(5));                    // inheritance: Manager.bonus reuses Employee.bonus
    }
}
Output
150.0

5.2The deque example in Java

Recall the example from Week 1. A queue has insert-rear and delete-front. A stack has insert-front and delete-front. A deque has all four: insert-front, delete-front, insert-rear, delete-rear.

RelationshipWhy
SubtypingDeque is a subtype of both Stack and Queuea deque has more functionality than a queue or a stack
InheritanceStack and Queue can inherit from Dequesuppress two of its functions and a deque works as a queue or a stack: the code is re-used
Deque is a subtype of both (Lecture 5)interface Stackinterface Queueclass Deque implementsStack, QueueIf class Stack extends Deque (code reuse):Stack inherits insertRear too, and Deque d = new Stack(); compiles.Reuse leaks into subtyping.Better (composition):class Stack { private Deque deque = new Deque(); push() pop() }Reuses the code, exposes only push and pop.
Figure 9. In Java, implements gives the subtyping; extends mixes in the reuse

Subtyping in Java: interfaces. Each interface lists what a stack or a queue can do. Deque implements both, so a Deque can be used wherever a Stack or a Queue is needed.

Stack.java
public interface Stack {
    void insertFront(int x);
    int deleteFront();
}
Queue.java
public interface Queue {
    void insertRear(int x);
    int deleteFront();
}
Deque.java
public class Deque implements Stack, Queue {
    private java.util.ArrayDeque<Integer> items = new java.util.ArrayDeque<>();

    public void insertFront(int x) { items.addFirst(x); }
    public int deleteFront()       { return items.removeFirst(); }
    public void insertRear(int x)  { items.addLast(x); }
    public int deleteRear()        { return items.removeLast(); }
}
Main.java
public class Main {
    public static void main(String[] args) {
        Stack s = new Deque();                  // a Deque used as a Stack
        s.insertFront(1);
        s.insertFront(2);
        System.out.println(s.deleteFront());     // last in, first out

        Queue q = new Deque();                  // a Deque used as a Queue
        q.insertRear(1);
        q.insertRear(2);
        System.out.println(q.deleteFront());     // first in, first out
    }
}
Output
2
1

Inheritance in Java: extends. It is tempting to build the stack by extending Deque and re-using all its code. But extends also makes the stack a subtype of Deque. The lecture's point appears: the interfaces are not compatible, yet the compiler lets it through.

LeakyStack.java
public class LeakyStack extends Deque {
    // reuses all of Deque: but it is also a Deque
}
Main.java
public class Main {
    public static void main(String[] args) {
        LeakyStack s = new LeakyStack();
        s.insertFront(1);
        s.insertFront(2);
        s.insertRear(99);                        // a "stack" that accepts rear insertion!
        Deque d = s;                             // and a LeakyStack is a Deque
        System.out.println(d.deleteRear());
    }
}
Output
99

Re-use without subtyping: composition. Keep a Deque as a private field and expose only what a stack should offer.

SafeStack.java
public class SafeStack {
    private Deque deque = new Deque();           // reuse by composition

    public void push(int x) { deque.insertFront(x); }
    public int pop()        { return deque.deleteFront(); }
}
Main.java
public class Main {
    public static void main(String[] args) {
        SafeStack s = new SafeStack();
        s.push(1);
        s.push(2);
        System.out.println(s.pop());
        s.insertRear(99);                        // not available
    }
}
Error
Main.java:7: error: cannot find symbol
        s.insertRear(99);                        // not available
         ^
  symbol:   method insertRear(int)
  location: variable s of type SafeStack
1 error

5.3Which tool for which job

You wantUseYou get
Subtyping only: others can rely on the capabilitiesinterface + implementssubstitutability, no code
Subtyping and code re-use, for a real is-a (Manager is an Employee)class B extends Aboth, together
Re-use only, no subtype (Stack from Deque)composition: a private field plus delegationthe code, with a small interface
Summary of the lecture. The class hierarchy gives both subtyping and inheritance. Subtyping is a relationship of capabilities, inheritance a relationship of code. A stack and a queue inherit from a deque, but it is the deque that is a subtype of both.

5.4Exam tips and common mistakes

Exam tips
  • Subtyping is about capabilities (interfaces); inheritance is about re-using code (implementations).
  • The subtype supports at least what the main type supports: Deque (4 operations) is the subtype of Stack (2).
  • In Java extends always gives both. To re-use without subtyping, use composition.
Common mistakes
  • Saying a Stack is a subtype of Deque because it inherits from it. It can use Deque's code but cannot stand in for it.
  • Using extends purely to save typing, when the relationship is not is-a.
  • Mixing up the direction: the subtype is the one with more capabilities.

5.5Interview corner: what they may ask

Likely questionChanceCrisp answer
Subtyping vs inheritance?Very likelySubtyping: a type can be used wherever another is expected (superset of capabilities). Inheritance: re-use of code. In Java extends gives both; implements gives subtyping only.
Inheritance vs composition? Which do you prefer?Very likelyInheritance for a true is-a where the child can always replace the parent. Otherwise composition: hold the object in a private field and delegate. Composition does not leak the parent's whole interface.
Why should a Stack not extend a Deque?Very likelyIt would inherit insertRear and deleteRear, so a "stack" could be used (or misused) as a deque, breaking last-in first-out. It is a code-reuse, not an is-a, relationship.
What is the Liskov substitution principle?LikelyWherever a parent is expected, any subtype must work without surprising the caller. A subclass that removes or breaks behaviour violates it.
When do you use an interface instead of a class?LikelyWhen you want to describe capabilities that unrelated classes can share, without sharing code or state.
Next · Lecture 6Java Modifiers
06
Lecture 6

Java Modifiers

public and private for encapsulation, static for things that exist without objects, and final for things that cannot change.

6.1Modifiers in Java

Java uses many modifiers in declarations, to cover different features of object-oriented programming. Three families do most of the work:

ModifierSupportsMeaning
public / privateencapsulation of datawho is allowed to use it
staticentities defined inside classes that exist without creating objectsbelongs to the class itself
finalvalues that cannot be changedassigned once

These modifiers can be applied to classes, instance variables and methods. Let us look at situations where the different combinations make sense.

6.2public versus private

  • A faithful implementation of encapsulation needs public and private.
  • Typically instance variables are private; the methods that query (accessor) and update (mutator) the state are public.
  • Sometimes we also use private methods: helpers that do some work which should not be directly accessible to the public.

Example: a Stack class

class Stackprivateint[] valuesint sizeint topboolean stack_full()void extend_stack()publicvoid push(int i)int pop()boolean isEmpty()the only way inoutside code cannot touch these
Figure 10. Private data and helpers inside, public operations outside

The data is stored in a private array, with public methods to push, pop and query isEmpty. push() must check whether there is space. To deal gracefully with stack overflow, it calls two private methods: one to check if the stack is full and one to extend the storage.

Stack.java
public class Stack {
    private int[] values = new int[2];
    private int size = 2;
    private int top = 0;

    public void push(int i) {
        if (stack_full()) {
            extend_stack();                    // deal gracefully with overflow
        }
        values[top] = i;
        top += 1;
    }

    public int pop() {
        if (isEmpty()) {
            throw new IllegalStateException("stack is empty");
        }
        top -= 1;
        return values[top];
    }

    public boolean isEmpty() {
        return top == 0;
    }

    private boolean stack_full() {
        return top == size;
    }

    private void extend_stack() {
        values = java.util.Arrays.copyOf(values, 2 * size);   // allocate more space
        size = 2 * size;
    }
}
Main.java
public class Main {
    public static void main(String[] args) {
        Stack s = new Stack();            // capacity starts at 2
        for (int i = 1; i <= 5; i++) {
            s.push(i);                    // the 3rd and 5th push make the stack grow, unseen
        }
        while (!s.isEmpty()) {
            System.out.print(s.pop() + " ");
        }
        System.out.println();
        s.pop();                           // popping an empty stack
    }
}
Error
5 4 3 2 1 
Exception in thread "main" java.lang.IllegalStateException: stack is empty
	at Stack.pop(Stack.java:16)

Callers never learn that the array doubled. And they cannot reach inside:

Main.java
public class Main {
    public static void main(String[] args) {
        Stack s = new Stack();
        s.extend_stack();
        System.out.println(s.top);
    }
}
Error
Main.java:4: error: extend_stack() has private access in Stack
        s.extend_stack();
         ^
Main.java:5: error: top has private access in Stack
        System.out.println(s.top);
                            ^
2 errors

6.3Accessor and mutator: keeping state consistent

Public methods to query and update private instance variables are good, but they can be too generous. The Date class with one setter per instance variable allows inconsistent updates: nothing stops us from separately setting an invalid combination of day and month.

Date.java
public class Date {
    private int day, month, year;

    public int getDay() { return day; }
    public int getMonth() { return month; }
    public int getYear() { return year; }

    public void setDay(int d) { day = d; }
    public void setMonth(int m) { month = m; }
    public void setYear(int y) { year = y; }
}
Main.java
public class Main {
    public static void main(String[] args) {
        Date d = new Date();
        d.setMonth(2);
        d.setDay(31);                       // February 31st
        System.out.println(d.getDay() + "/" + d.getMonth());
    }
}
Output
31/2

Instead, only allow a combined update, and validate the combination inside it:

Date.java
public class Date {
    private int day, month, year;

    public int getDay() { return day; }
    public int getMonth() { return month; }
    public int getYear() { return year; }

    public boolean setDate(int d, int m, int y) {
        if (m < 1 || m > 12 || d < 1 || d > daysIn(m, y)) {
            return false;                       // validate d-m-y combinations
        }
        day = d;
        month = m;
        year = y;
        return true;
    }

    private static int daysIn(int m, int y) {   // a private helper
        if (m == 2) {
            boolean leap = (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
            return leap ? 29 : 28;
        }
        return (m == 4 || m == 6 || m == 9 || m == 11) ? 30 : 31;
    }
}
Main.java
public class Main {
    public static void main(String[] args) {
        Date d = new Date();
        System.out.println(d.setDate(31, 2, 2023));    // rejected
        System.out.println(d.setDate(29, 2, 2024));    // 2024 is a leap year
        System.out.println(d.setDate(31, 4, 2024));    // April has 30 days
        System.out.println(d.getDay() + "/" + d.getMonth() + "/" + d.getYear());
    }
}
Output
false
true
false
29/2/2024
Design lesson. A setter per field is not automatically good encapsulation. Offer the operations the class really supports, and let it protect its own invariants.

6.4static components

  • Use static for components that exist without creating objects: library functions, main(), and useful constants like Math.PI and Integer.MAX_VALUE. These static components are also public.
  • We do use private static sometimes, for internal bookkeeping. Example: the constructor gives each order a unique id.
class Orderstatic lastorderidONE copy, = 3order 1orderid = 1order 2orderid = 2order 3orderid = 3each object has its OWN orderid (instance variable)Order() { lastorderid++; orderid = lastorderid; }each constructor call bumps the shared counter, then copies it into its own orderidstatic = belongs to the class (no object needed); private static = hidden bookkeeping.Concurrent updates (several threads) need extra care.
Figure 11. One shared counter, one orderid per object
Order.java
public class Order {
    private static int lastorderid = 0;      // one copy, shared by every Order
    private int orderid;                     // one copy per Order

    public Order() {
        lastorderid++;
        orderid = lastorderid;
    }

    public int getOrderId() {
        return orderid;
    }
}
Main.java
public class Main {
    public static void main(String[] args) {
        Order a = new Order();
        Order b = new Order();
        Order c = new Order();
        System.out.println(a.getOrderId() + " " + b.getOrderId() + " " + c.getOrderId());
        System.out.println(Math.PI);                  // a static constant: no object needed
        System.out.println(Integer.MAX_VALUE);
    }
}
Output
1 2 3
3.141592653589793
2147483647
  • lastorderid is a private static field: common to all objects in the class, hidden from outside.
  • Concurrent updates need some care: if two threads create orders at the same moment, lastorderid++ can give both the same id. (We meet threads later.)
  • A static method belongs to the class, so it has no this and cannot touch instance variables directly:
Main.java
public class Main {
    int count = 0;                       // an instance variable

    public static void main(String[] args) {
        count = count + 1;               // main is static: which object's count?
    }
}
Error
Main.java:5: error: non-static variable count cannot be referenced from a static context
        count = count + 1;               // main is static: which object's count?
        ^
Main.java:5: error: non-static variable count cannot be referenced from a static context
        count = count + 1;               // main is static: which object's count?
                ^
2 errors

6.5final components

  • final denotes that a value cannot be updated. It is usually used for constants: public static final instance variables such as Math.PI and Integer.MAX_VALUE.
  • What would final mean for a method? It cannot be redefined by a subclass. The lecture's summary says the same: a final method cannot be overridden.
Main.java
public class Main {
    public static final double RATE = 0.05;     // a constant

    public static void main(String[] args) {
        System.out.println(RATE * 1000);
        RATE = 0.10;                             // not allowed
    }
}
Error
Main.java:6: error: cannot assign a value to static final variable RATE
        RATE = 0.10;                             // not allowed
        ^
1 error
Main.java
public class Main {
    public static void main(String[] args) {
        Manager m = new Manager("A", 1, "B");
    }
}
Error
Manager.java:18: error: bonus(float) in Manager cannot override bonus(float) in Employee
    public double bonus(float percent) {      // overrides Employee.bonus
                  ^
  overridden method is final
1 error
Main.java
public class Main {
    public static void main(String[] args) {
        Manager m = new Manager("A", 1, "B");
    }
}
Error
Manager.java:1: error: cannot inherit from final Employee
public class Manager extends Employee {
                             ^
1 error
  • A final class cannot be extended at all. String is a final class, which is part of why strings are safe to share.
  • final on a variable fixes which object it refers to, not the object's contents (extra):
Main.java
public class Main {
    public static void main(String[] args) {
        final int[] a = {1, 2, 3};
        a[0] = 99;                       // allowed: the contents can change
        System.out.println(a[0]);
        a = new int[3];                  // not allowed: the reference cannot
    }
}
Error
Main.java:6: error: cannot assign a value to final variable a
        a = new int[3];                  // not allowed: the reference cannot
        ^
1 error

6.6Putting the modifiers together

CombinationTypical useExample
private instance variablehidden stateprivate double salary;
public methodthe interfacepublic double bonus(float p)
private methodinternal helperprivate boolean stack_full()
public staticlibrary function, mainpublic static void main(String[] args)
private staticbookkeeping shared by all objectsprivate static int lastorderid;
public static finala constantMath.PI, Integer.MAX_VALUE
final methodcannot be overriddenpublic final void audit()

The lecture ends: we can also have private classes. A top-level class cannot be private, but a class nested inside another can, so it stays an internal detail:

Helper.java
private class Helper {
}
Error
Helper.java:1: error: modifier private not allowed here
private class Helper {
        ^
1 error
Main.java
public class Main {
    private static class Helper {            // nested, and private to Main
        int twice(int x) { return 2 * x; }
    }

    public static void main(String[] args) {
        System.out.println(new Helper().twice(21));
    }
}
Output
42
Summary of the lecture
  • private and public are natural artefacts of encapsulation. Usually instance variables are private and methods are public, but private methods also make sense.
  • static and final are orthogonal to public/private: you can combine them freely.
  • Use private static variables to keep bookkeeping across the objects of a class: a global serial number, a count of objects created, profiling method calls.
  • final is usually used for constants. A final method cannot be overridden by a subclass.

6.7Exam tips and common mistakes

Exam tips
  • Default recipe: private fields, public methods. A private method is a helper that outsiders should not call.
  • static = one per class; non-static = one per object.
  • final variable = assigned once; final method = not overridable; final class = not extendable.
Common mistakes
  • Using an instance variable from a static method (main) without an object.
  • Believing a final array or object cannot change inside: only the reference is fixed.
  • Making every field public "for convenience", or giving every field a setter.
  • Forgetting that a static counter is shared across all objects (and is not thread-safe by itself).

6.8Interview corner: what they may ask

Likely questionChanceCrisp answer
static vs non-static?Very likelystatic members belong to the class and exist once, without any object; non-static (instance) members belong to each object. A static method has no this.
What does final mean on a variable, a method, a class?Very likelyVariable: assigned once (a constant). Method: cannot be overridden. Class: cannot be extended.
public, private, protected and default?Very likelyprivate: same class only. Default (none): same package. protected: same package and subclasses. public: everywhere.
Why is main static?Very likelyThe JVM must call it before any object exists, so it belongs to the class.
How would you give every object a unique id?LikelyA private static counter that every constructor increments and copies into an instance field. Add synchronisation or an AtomicInteger if threads create objects.
Is a final list or array immutable?LikelyNo. final fixes the reference, not the contents. Immutability needs a class with no mutators.
Why use private methods?LikelyFor internal helpers (like stack_full()) that are part of the implementation, not the interface, so they can change freely.
Next · InterviewInterview Question Bank
07
Interview

Interview Question Bank

What is likely to be asked from Week 3, with 30-second answers, deeper explanations, a lecture-or-extra tag on each question, and code-reading practice.

Week 3 is where interviewers test whether you really understand inheritance and polymorphism. Every answer starts with a 30-second version. Each question is tagged From the lecture or Extra, so you know which answers go beyond what the lecture said.

1Definition

One clear sentence: what it is.

2Tiny example

Three or four lines of code.

3The failure it prevents

What goes wrong without it.

7.1Interview heat-map: what is most likely

Very likely8 topics
Overloading vs overriding"Explain with an example."Notes 3.4
Runtime polymorphism and dynamic dispatch"Which method runs for Employee e = new Manager()?"Notes 3.2, 3.3
== vs equals(), overriding equals and hashCode"How do you write a correct equals?"Notes 4.4, 4.5
Inheritance vs composition, subtyping vs inheritance"Should Stack extend Deque?"Notes 5.2, 5.3
Why no multiple inheritance"What is the diamond problem?"Notes 4.1
super and constructors in inheritance"In what order are constructors called?"Notes 2.3
static vs instance, final, access modifiers"What does final mean on a method? A class?"Notes 6.4, 6.5
The Object class: toString, equals, hashCode"What does every class inherit?"Notes 4.2, 4.3
Likely4 topics
Casting, instanceof, ClassCastException"When does a cast fail?"Notes 3.5
@Override and the rules of overriding"Can you weaken access when overriding?"Notes 3.1
Encapsulation and consistent state"Why not a setter for every field?"Notes 6.3
Interfaces and default-method conflicts"How do interfaces avoid the diamond problem?"Notes 4.1
Sometimes2 topics
OO vs procedural design, coupling, aggregation"How do you find the classes in a problem?"Notes 1.1 - 1.4
static counters and threads"Is lastorderid++ thread safe?"Notes 6.4

7.2Design and inheritance

Interview 1Procedural vs object-oriented design: how do you find the classes?
Lecture 1Very likelyNotes 1.1 - 1.2From the lecture

Start from Wirth's title, Algorithms + Data Structures = Programs.

Answer
Say this first (30 seconds)

Procedural design puts algorithms first and lets procedures share data. OO design puts the data first, groups it with the methods that operate on it into classes, and finds those classes by reading the requirements: nouns are objects, verbs are methods.

Go deeper
  • It scales: 100 classes of about 20 methods is easier to grasp, and an object in a bad state is debugged by searching 20 methods, not 2000 procedures.
Follow-up they may askName the four things you decide when designing an object.
Interview 2Dependence, aggregation and inheritance: what is the difference?
Lecture 1LikelyNotes 1.4From the lecture

Use Order, Item, Account, ExpressOrder.

Answer
Say this first (30 seconds)

Dependence: one class uses another (Order needs Account for credit). Aggregation: one object contains others (Order has Items). Inheritance: one class is a specialised version of another (ExpressOrder is an Order).

Go deeper
  • A robust design minimises dependencies (coupling) so a change stays local.
Interview 3What does a subclass inherit, and in what order are constructors called?
Lecture 2Very likelyNotes 2.2 - 2.3From the lecture

Constructors are not inherited; they are chained.

Main.java
class A { A() { System.out.println("A"); } }
class B extends A { B() { System.out.println("B"); } }
new B();
Prints
A
B
Answer
Say this first (30 seconds)

A subclass inherits the parent's non-private fields and methods and adds its own, but it cannot see the parent's private members. A constructor always starts by calling a parent constructor with super(...), so the parent is built first.

Go deeper
  • Without an explicit super(...) Java adds super(); if the parent has no no-argument constructor, it does not compile.
Follow-up they may askWhat does this(...) do, and can you use both this(...) and super(...)?

7.3Polymorphism and dispatch

Interview 4Which method runs for Employee e = new Manager(...); e.bonus(5)? Why can't you call e.setSecretary()?
Lecture 3Very likelyNotes 3.2From the lecture

Static type versus runtime type.

Answer
Say this first (30 seconds)

The compiler checks the declared (static) type: Employee has no setSecretary, so that call does not compile. The JVM picks bonus from the real (runtime) type, Manager: dynamic dispatch, the default in Java.

Go deeper
  • In C++ it is optional and needs virtual. In Java every non-static, non-private, non-final method is dispatched dynamically.
Follow-up they may askDoes dispatch work for fields? For static methods?
Interview 5Overloading versus overriding.
Lecture 3Very likelyNotes 3.4From the lecture

One table, one example each.

Main.java
static void describe(Employee x) { ... }
static void describe(Manager x)  { ... }
Employee e = new Manager(...);
describe(e);                 // describe(Employee): picked from the static type
Answer
Say this first (30 seconds)

Overloading: same name, different parameter lists, in one class, chosen at compile time from the argument types (Arrays.sort(int[]) vs sort(double[])). Overriding: a subclass redefines an inherited method with the same signature, chosen at run time from the object's class.

Go deeper
  • @Override turns a silent overload (a typo in the parameter type) into a compile error.
Follow-up they may askWhat happens if two overloads both fit a call?
Interview 6What is a ClassCastException, and how do instanceof and casting work?
Lecture 3Very likelyNotes 3.5From the lecture

Say when it happens, then how to avoid it.

Answer
Say this first (30 seconds)

A cast to a subclass is checked at run time. If the object is not really an instance of that class the JVM throws ClassCastException. Guard it with instanceof, or avoid the cast by using polymorphism.

Go deeper
  • Casting an int like (int) 29.98 is different: it converts the value (to 29).
  • Java 16+: if (e instanceof Manager m) { ... } tests and casts in one step (extra).
Interview 7What are the rules for overriding a method?
Lecture 3 + extraLikelyNotes 3.1Extra: often asked

Signature, access, return type, @Override.

Answer
Say this first (30 seconds)

Same name and parameter types; access no weaker than the original (a missing public is a compile error); return type the same or a subtype. Add @Override so the compiler checks it. final methods cannot be overridden.

Go deeper
  • private and static methods are not overridden (a same-named method in the subclass just hides or is unrelated).

7.4The Object class

Interview 8== versus equals(): and is it true that x == y calls x.equals(y)?
Lecture 4 + correctionVery likelyNotes 4.4Extra: often asked

The lecture's slide says it does. Say what is really true.

Main.java
System.out.println(find(dates, target));       // equals(): found
System.out.println(findSame(dates, target));   // ==: not found
Prints
1
-1
Answer
Say this first (30 seconds)

No. == on objects always compares references (are they the same object?) and never calls equals. equals compares whatever the class defines, usually state; the default Object.equals is just ==.

Go deeper
  • A generic search must call objarr[i].equals(o); with == it finds only the identical object.
Follow-up they may askWhy does "hi" == "hi" sometimes give true?
Interview 9How do you override equals correctly? What else must you override?
Lecture 4 + extraVery likelyNotes 4.5Extra: often asked

The lecture's trap: equals(Date d) does not override.

Answer
Say this first (30 seconds)

Take an Object parameter, check instanceof, cast, compare the fields, and add @Override. Also override hashCode so equal objects have equal hash codes.

Go deeper
  • equals(Date d) is an overload: code that only knows Object never reaches it.
  • Without hashCode, a HashSet can keep two "equal" objects.
Follow-up they may askName the properties of a good equals (reflexive, symmetric, transitive).
Interview 10Why does Java not allow multiple inheritance? How do interfaces help?
Lecture 4Very likelyNotes 4.1From the lecture

Draw the diamond.

Answer
Say this first (30 seconds)

If C1 and C2 both define f() and C3 extends both, which f() does C3 use? To avoid that ambiguity, a Java class extends exactly one class. A class may implement several interfaces, and if two supply the same default method, it must override it and choose.

Go deeper
  • So the class hierarchy is a tree rooted at Object.
Interview 11What does every class inherit from Object? What does the default toString() print?
Lecture 4LikelyNotes 4.2 - 4.3From the lecture

equals, toString, hashCode, getClass.

Answer
Say this first (30 seconds)

Every class extends Object, so it has equals (pointer equality by default), toString, hashCode and getClass. The default toString prints the class name, @ and a hash code, so override it to show the state; println and string concatenation then use your version.

Go deeper
  • This works because of dynamic dispatch: println only knows Object.

7.5Subtyping and composition

Interview 12Subtyping vs inheritance, with the deque, stack and queue.
Lecture 5Very likelyNotes 5.1 - 5.2From the lecture

Which one is the subtype of which?

Answer
Say this first (30 seconds)

Subtyping is about capabilities: a deque (4 operations) is a subtype of both stack (2) and queue (2) because it can do everything they can. Inheritance is code re-use: a stack and a queue can inherit from a deque by suppressing two functions.

Go deeper
  • In Java extends gives both, implements gives subtyping only.
Follow-up they may askWhich relationship does Python's class syntax make you choose?
Interview 13Should a Stack extend a Deque? Inheritance or composition?
Lecture 5 + extraVery likelyNotes 5.2Extra: often asked

Run the thought experiment: what could a caller do with a Deque variable holding a stack?

Answer
Say this first (30 seconds)

No. extends would make the stack a subtype of Deque, so it inherits insertRear and deleteRear and can be misused as a deque. Hold a Deque in a private field and delegate push and pop: the code is re-used and the interface stays small.

Go deeper
  • Rule of thumb: favour composition over inheritance; inherit only for a true is-a that can always replace the parent.

7.6Modifiers

Interview 14static versus instance members. How do you give every object a unique id?
Lecture 6Very likelyNotes 6.4From the lecture

The lecture's lastorderid example.

Main.java
Order a = new Order();
Order b = new Order();
Order c = new Order();
System.out.println(a.getOrderId() + " " + b.getOrderId() + " " + c.getOrderId());
Prints
1 2 3
Answer
Say this first (30 seconds)

A static member belongs to the class: one copy, no object needed. An instance member belongs to each object. For ids, keep a private static counter that every constructor increments and copies into the object's own orderid.

Go deeper
  • A static method has no this, so it cannot use instance variables directly.
  • lastorderid++ is not atomic: concurrent threads need care (synchronized or AtomicInteger).
Follow-up they may askCan a static method be overridden?
Interview 15What does final mean on a variable, a method and a class?
Lecture 6Very likelyNotes 6.5From the lecture

Three uses, one idea: "cannot change".

Answer
Say this first (30 seconds)

A final variable can be assigned only once (a constant, e.g. Math.PI). A final method cannot be overridden. A final class cannot be extended (String is one).

Go deeper
  • final on a reference fixes which object it points to, not the object's contents. final int[] a still allows a[0] = 99.
Interview 16public, private, protected, default: and why private methods?
Lecture 6Very likelyNotes 6.2From the lecture

Give the four levels and a reason for private helpers.

Answer
Say this first (30 seconds)

private: same class only. Default (no modifier): same package. protected: same package plus subclasses. public: everyone. Private methods hold implementation helpers like stack_full() that are not part of the interface, so they can change freely.

Go deeper
  • A setter per field can still leave an object inconsistent (31 February). Offer one validating setDate(d, m, y) instead.

7.7Code-reading questions to expect

All outputs below come from really compiling and running the code. Try each before opening the answer.

Trace AStatic type or runtime type?
Code readingVery likelyNotes 3.2, 3.5
Main.java
Employee e = new Manager("Ravi", 2000, "Meena");
Employee f = new Employee("Asha", 2000);
System.out.println(e.bonus(5) + " " + f.bonus(5));
System.out.println(e instanceof Manager);
System.out.println(f instanceof Manager);
Answer
Prints
150.0 100.0
true
false
  • Same declared type, same salary, different objects: e.bonus(5) runs Manager.bonus (1.5 times), f.bonus(5) runs Employee.bonus.
  • instanceof looks at the runtime object.
Trace BOverload or override?
Code readingVery likelyNotes 4.5
Main.java
Date d1 = new Date(1, 2, 2000);
Date d2 = new Date(1, 2, 2000);
Object o2 = d2;
System.out.println(d1.equals(d2));
System.out.println(d1.equals(o2));
Answer
Prints
true
false
  • equals(Date) is chosen when the static type of the argument is Date: true.
  • With an Object-typed argument only the inherited equals(Object), which is ==, fits: false.
Trace CShared or per object?
Code readingVery likelyNotes 6.4
Main.java
Counter a = new Counter();
Counter b = new Counter();
a.hit();
a.hit();
b.hit();
System.out.println(a.mine + " " + b.mine + " " + Counter.total);
Answer
Prints
2 1 3
  • mine is per object: 2 and 1. total is shared by the class: 3.
Trace DWhich Stack methods can a caller reach?
Code readingLikelyNotes 5.2
Main.java
LeakyStack s = new LeakyStack();
s.insertFront(1);
s.insertFront(2);
s.insertRear(99);
Deque d = s;
System.out.println(d.deleteRear());
Answer
Prints
99
  • LeakyStack inherits all four operations, so the "stack" accepts insertRear, and deleteRear returns the 99 that a real stack could never have put at the bottom. It is a subtype of Deque whether we wanted that or not.
Next · SummaryWeek 3 Summary Sheet
08
Summary

Week 3 Summary Sheet

Week 3 on one page: a lecture-by-lecture glance, key terms, the pairs to tell apart, the errors you can now read, a checklist and corrections to the source notes.

8.1Week 3 at a glance

L1Philosophy of OO

Data first, then algorithms. Nouns are objects, verbs are methods. Design behaviour, state, encapsulation, identity.

L2Subclasses and inheritance

extends inherits fields and methods; private stays hidden; super(...) builds the parent part.

L3Dynamic dispatch

Overriding picks the method from the runtime type; overloading from the static types. Cast with instanceof.

L4The class hierarchy

No multiple inheritance, a tree under Object. equals(Object), toString(), hashCode().

L5Subtyping vs inheritance

Subtyping = capabilities, inheritance = code. implements gives the first, extends both, composition only re-use.

L6Modifiers

public/private encapsulate, static belongs to the class, final cannot change.

8.2Key terms, lecture by lecture

L1 · Philosophy of OO
Behaviour
The methods that operate on the objects.
State
The information in the instance variables.
Identity
What distinguishes two objects of the same class.
Dependence / coupling
One class needs another; keep it low.
Aggregation
One object contains others.
L2 · Subclasses and inheritance
extends
Makes a class a subclass of another.
super
The parent part: super(...) constructor, super.m() method.
Subclass / parent
The specialised class / the class it extends.
L3 · Dynamic dispatch
Override
Redefine an inherited method, same signature.
Dynamic dispatch
The runtime type picks the method.
Signature
Name plus argument types.
Overload
Same name, different signatures, chosen at compile time.
instanceof
Tests the runtime class of an object.
L4 · The class hierarchy
Object
The root class of every class.
equals()
Pointer equality by default; override with an Object parameter.
toString()
Describes the state; used by println and +.
hashCode()
Must agree with equals.
L5 · Subtyping vs inheritance
Subtyping
Capabilities are a superset: usable wherever the main type is.
Inheritance
Re-use of code.
Composition
Hold an object in a private field and delegate.
L6 · Modifiers
public / private
Visible to all / only to the class.
static
Belongs to the class; exists without objects.
final
Cannot be reassigned / overridden / extended.

8.3The pairs you must tell apart

Overloadingdifferent signatures, compile time
VS
Overridingsame signature, run time
Static typedeclared; the compiler checks it
VS
Runtime typereal object; picks the method
==same object (identity)
VS
equals()same state, if overridden
equals(Object)overrides
VS
equals(Date)only an overload
Subtypingcapabilities
VS
Inheritancecode re-use
extendssubtyping + code
VS
implementssubtyping only
Inheritanceis-a, one parent
VS
Compositionhas-a, re-use only
Aggregationcontains (has-a)
VS
Dependenceuses (keep it low)
staticone per class
VS
instanceone per object
final variableassigned once
VS
final method / classno override / no extending
privatethis class only
VS
protectedpackage + subclasses
Upcastchild to parent: automatic
VS
Downcastneeds a cast; may throw at run time
super(...)parent constructor, first line
VS
this(...)another constructor of this class

8.4Messages and errors you can now read

What went wrongWhat you will seeWhen
subclass constructor without a usable super(...)error: constructor Employee in class Employee cannot be applied to given types;compile time
subclass touching the parent's private fielderror: name has private access in Employeecompile time
child variable given a parent objecterror: incompatible types: Employee cannot be converted to Managercompile time
calling a subclass-only method through a parent variableerror: cannot find symbolcompile time
override with weaker accesserror: bonus(float) in Manager cannot override bonus(float) in Employeecompile time
@Override on something that overrides nothingerror: method does not override or implement a method from a supertypecompile time
5.0 passed to a float parametererror: incompatible types: possible lossy conversion from double to floatcompile time
extending two classeserror: '{' expectedcompile time
two interfaces with the same default methoderror: types I1 and I2 are incompatible;compile time
overriding a final methodoverridden method is finalcompile time
extending a final classerror: cannot inherit from final Employeecompile time
assigning a final variable againerror: cannot assign a value to final variable acompile time
instance variable used in a static methoderror: non-static variable count cannot be referenced from a static contextcompile time
top-level class marked privateerror: modifier private not allowed herecompile time
cast to a class the object is notjava.lang.ClassCastException: class Employee cannot be cast to class Managerrun time
popping an empty stackjava.lang.IllegalStateException: stack is emptyrun time

8.5Last-minute checklist

L1 · Philosophy of OO
  • Algorithms first vs data first
  • Nouns are objects, verbs are methods
  • Behaviour, state, encapsulation, identity
  • Dependence, aggregation, inheritance
L2 · Subclasses and inheritance
  • extends and what is inherited
  • Private is hidden from the subclass
  • super(...) first in a constructor
  • Parent variable can hold a child, not vice versa
L3 · Dynamic dispatch
  • Overriding rules, @Override
  • Static type checked, runtime type dispatched
  • Overloading vs overriding
  • Casting and instanceof
L4 · The class hierarchy
  • No multiple inheritance; the diamond
  • Everything extends Object
  • toString()
  • equals(Object) vs ==, and hashCode
L5 · Subtyping vs inheritance
  • Subtyping is capabilities, inheritance is code
  • Deque is the subtype of Stack and Queue
  • implements vs extends vs composition
L6 · Modifiers
  • Private fields, public methods, private helpers
  • One validating setter beats many setters
  • static: one per class, lastorderid
  • final on variable, method, class

8.6Corrections made to the source notes

WhereThe source saysCorrect
Lecture 2, Employeepublic String getSalary()a salary is a double: public double getSalary()
Lecture 3, Managerpublic class Manager {needs extends Employee
Lecture 3, the loopemparray[i].bonus(5.0)does not compile for a float parameter: use 5.0f (checked by running)
Lecture 3, overriding"choice is static"overriding is resolved at run time (dynamic dispatch)
Lecture 4, findobjarr.length()objarr.length: a field (checked by running)
Lecture 4, equality"x == y invokes x.equals(y)"false: == always compares references. Call equals yourself (checked by running)
Lecture 4, typosm1.equals()m2m1.equals(m2)
Lecture 6, pushtop += 1missing semicolon: top += 1;
Revision overview
Designdata first
nouns and verbs
coupling, aggregation
Inheritanceextends, super(...)
private stays hidden
Parent p = new Child()
Dispatchoverride: runtime
overload: compile time
cast + instanceof
Week 3OO in Java
Objecttree under Object
equals, toString
no multiple inheritance
Subtypingcapabilities vs code
implements / extends
composition
Modifierspublic / private
static: per class
final: no change
Revision: all of Week 3 on one page
Back to top