dev only

Object Superclass

Every Class's Ultimate Parent

Object (from java.lang) is the superclass of every class in Java — any class without an explicit extends inherits from it automatically. Two of Object's methods matter enough to override in almost every class you write: toString() and equals(Object other).

Overriding toString()

Object's default toString() produces an unhelpful string like Subsystem@1a2b3c (the class name plus a memory-address-derived hash). Overriding it to describe the object's real state is good practice in nearly every class:

public class Subsystem
{
    private String name;

    public Subsystem(String name) { this.name = name; }

    @Override
    public String toString() { return name; }
}

A subclass's own toString() can call super.toString() and build on it, rather than starting from scratch:

public class Intake extends Subsystem
{
    private boolean hasGamePiece;

    public Intake(String name) { super(name); }

    @Override
    public String toString()
    {
        return super.toString() + " (hasGamePiece=" + hasGamePiece + ")";
    }
}

The equals() Method

equals(Object other), inherited from Object, tests whether two references point to the exact same object — the same result as == (Lesson 13's "== vs. .equals()" gotcha exists precisely because many classes, like String, override this default with something more useful).

Overriding equals() Correctly

Overriding equals is more involved than toString, since it has to satisfy five formal requirements to remain safe to use anywhere: reflexive (o.equals(o) is always true), symmetric (a.equals(b) matches b.equals(a)), transitive (a.equals(b) and b.equals(c) implies a.equals(c)), consistent (repeated calls give the same answer, if nothing changed), and never equal to null.

A standard recipe satisfies all five:

@Override
public boolean equals(Object other)
{
    if (this == other) return true;               // 1. quick check: same object
    if (!(other instanceof Subsystem)) return false; // 2. must be the same type
    Subsystem o = (Subsystem) other;               // 3. safe to cast now
    return this.name.equals(o.name);               // 4. compare the relevant fields
}

Note the parameter type is always Object, never the class itself — equals(Subsystem other) would be a completely different method (overloading, not overriding), not the one Object actually declares.

The Subclass equals() Trap

Overriding equals again in a subclass of a class that's already overridden it is genuinely difficult to get right, and easy to accidentally break symmetry. If Intake (subclass of Subsystem) overrode equals to also compare hasGamePiece, then genericSubsystem.equals(intakeObj) would call Subsystem's equals (comparing only name), while intakeObj.equals(genericSubsystem) would call Intake's equals — which checks other instanceof Intake and returns false, since a plain Subsystem isn't an Intake. The two calls disagree, breaking the symmetry rule. The general guideline: only one class in a hierarchy should provide a real equals override.

Common Pitfalls

  • Writing equals(Subsystem other) instead of equals(Object other). That's overloading a new method, not overriding Object's — code calling .equals() on a Subsystem-typed variable would still use Object's original version.
  • Forgetting the instanceof check before casting. Casting other to your class without checking first throws ClassCastException if other turns out to be some unrelated type.
  • Overriding equals again further down an inheritance hierarchy. This tends to violate symmetry — safest to let only one class in the hierarchy own the equals override.

Key Takeaways

  • Object is the superclass of every class in Java; toString() and equals(Object other) are its two methods worth overriding almost everywhere.
  • A subclass's toString() can call super.toString() and extend it, rather than rebuilding the whole description from scratch.
  • Object's default equals is identity comparison (same as ==); a correct override must be reflexive, symmetric, transitive, consistent, and never true against null.
  • The standard equals recipe: check this == other, check instanceof, cast, then compare the relevant fields — always with parameter type Object.
  • Avoid overriding equals more than once down an inheritance hierarchy — it tends to break symmetry.

Derived from csawesome-2026: unit-5-inheritance-optional/5.07-object-superclass.md (CSA 5.7)