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 ofequals(Object other). That's overloading a new method, not overridingObject's — code calling.equals()on aSubsystem-typed variable would still useObject's original version. - Forgetting the
instanceofcheck before casting. Castingotherto your class without checking first throwsClassCastExceptionifotherturns out to be some unrelated type. - Overriding
equalsagain further down an inheritance hierarchy. This tends to violate symmetry — safest to let only one class in the hierarchy own theequalsoverride.
Key Takeaways
Objectis the superclass of every class in Java;toString()andequals(Object other)are its two methods worth overriding almost everywhere.- A subclass's
toString()can callsuper.toString()and extend it, rather than rebuilding the whole description from scratch. Object's defaultequalsis identity comparison (same as==); a correct override must be reflexive, symmetric, transitive, consistent, and nevertrueagainstnull.- The standard
equalsrecipe: checkthis == other, checkinstanceof, cast, then compare the relevant fields — always with parameter typeObject. - Avoid overriding
equalsmore 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)