SwadhyAI Notes

All Java notes

Java Class Relationships

›Short versionOpen if you only have a minute.
  • Inheritance is is-a. The subclass must be usable as the superclass. A class extends one class.
  • Association means one object holds a reference and can call the other. It does not decide lifetime.
  • Aggregation is a shared part: the department has employees, and the employees outlive the department.
  • Composition is a owned part: the order creates its lines, and the lines go away with the order.
  • A dependency is a type you use and do not keep, usually a parameter. It is the lightest link.
  • Java does not encode aggregation versus composition in the type system. You encode it by who constructs the part and who is allowed to keep a reference.

An order lists three lines, names a customer, and is printed by a formatter that the order never stores. Those are three different relationships, and only one of them is . Getting the others wrong is how a line item becomes a subclass of order, or how a customer is destroyed because an order was cancelled.

Is-a is a promise about substitution

SavingsAccount extends Account tells every method that accepts an Account that a savings account is a legal argument. If a savings account cannot honor withdraw, the extends clause is a false sentence. Keep inheritance for that sentence, and for the overridden behavior that still respects it.

java
class SavingsAccount extends Account {
    SavingsAccount(int opening) {
        super(opening);
    }
}

Has-a, with a question about lifetime

An is the field. decides whether the field is one reference or a collection. Lifetime decides between and .

java
class Department {
    private final List<Employee> employees = new ArrayList<>();

    void add(Employee employee) {
        employees.add(employee);
    }
}

The department did not construct the employee, and removing the department should not erase the person. That is aggregation. The employee reference is shared. Another department, or a directory, may hold it too.

java
class Order {
    private final List<OrderLine> lines = new ArrayList<>();

    void addLine(String sku, int quantity) {
        lines.add(new OrderLine(sku, quantity));
    }

    List<OrderLine> lines() {
        return List.copyOf(lines);
    }
}

The order constructs each line and returns a copy. Callers cannot keep the live list and cannot invent a line the order did not check. When the order becomes garbage, nothing else should still need those line objects. That is composition. The language will not stop you from stashing a line in a static list. The design stops you by not handing the live reference out.

Uses, and then forgets

A does not deserve a field. The formatter is needed while the method runs.

java
String render(Order order, Clock clock) {
    return order.total() + " at " + clock.instant();
}

Storing the clock on the order would say the order has a clock for the rest of its life. The parameter says the method needs a clock once. Tests can pass a fixed clock without the order growing a time dependency.

The choice that hurts most is inheritance used as reuse. If Order extended ArrayList to "be a list of lines," every List method would become part of an order. Callers could clear the list and skip the order's checks. A private list inside the order keeps ArrayList as a tool and leaves Order as the type callers see.

A Playlist has Songs. Deleting the playlist should not delete the songs, because other playlists use them. Which relationship is that?

Aggregation. The playlist stores song references it did not exclusively create, and the songs outlive it. Composition would fit a playlist that owned private clip objects nobody else could name. Inheritance would fit only if a playlist were a kind of song, which it is not.