Inheritance
Every time you write public class Robot extends TimedRobot, you use inheritance. Robot inherits WPILib's entire lifecycle management — the 20ms loop, the mode-switching logic, the Driver Station communication — without writing a line of it. This lesson explains what that inheritance gives you, what it costs, and how to use super and @Override correctly.
By the end of this lesson, you will:
- Use the
extendskeyword to create a subclass that inherits a superclass's fields and methods - Call superclass constructors with
super()and superclass methods withsuper.method() - Override a superclass method with
@Overrideand explain what happens to the original - Navigate the WPILib inheritance chain from
Objectdown to yourRobot.java - Explain when to prefer composition over inheritance for robot subsystems
What Inheritance Gives You
Inheritance lets a class (the subclass) absorb all the fields and methods of another class (the superclass), then add its own or replace existing ones. The subclass is-a kind of the superclass — a Robot is a TimedRobot, an IntakeSubsystem is a SubsystemBase.
The practical value in FRC: WPILib's authors wrote all the hard scheduling infrastructure once. Every team's robot inherits it. You only write the parts that are specific to your robot — the periodic logic, the mechanisms, the autonomous routines.
The WPILib Inheritance Tree
Click any node to see what that class contributes and what it requires the subclass to provide.
The extends Keyword
super — Calling the Parent
super gives you a way to reach up into the superclass from within the subclass. It has two uses:
A subclass constructor must call the superclass constructor — directly or implicitly — before doing anything with the object. If you don't write super(), Java inserts an implicit super() with no arguments. If the superclass has no no-argument constructor, you must call the correct super(…) explicitly.
Inside an overridden method, super.methodName() calls the original superclass implementation. This is useful when you want to extend the parent behavior rather than replace it entirely.
@Override — Replacing a Superclass Method
When a subclass defines a method with the same name and parameter list as a superclass method, the subclass version replaces the superclass version for that object. The @Override annotation is technically optional but should always be included — it makes the compiler verify that you're actually overriding something, catching typos before they become runtime surprises.
One of the most reliable ways to produce a robot that does nothing in autonomous is a typo in the method name — autonomousPeroidic instead of autonomousPeriodic. Without @Override, this compiles silently and the method is never called. The fix is one annotation that would have prevented the entire problem. I recommend requiring @Override on every method that overrides a superclass method as a team coding standard enforced by your linter.
Inheritance vs. Composition
Inheritance is the right tool when your class genuinely is-a kind of the parent class. A Robot is a TimedRobot. An IntakeSubsystem is a SubsystemBase. These relationships are stable and the inherited behavior is genuinely useful.
Composition is often better when you want to reuse behavior without the is-a relationship. Instead of inheriting a motor configuration class, your subsystem has-a motor and calls its methods directly. This is what every subsystem already does with hardware objects.
An IntakeSubsystem is a SubsystemBase → inherit. An IntakeSubsystem has a TalonFX → compose. When inheritance would expose methods from the parent class that callers shouldn't have access to, or when you're inheriting solely for code reuse rather than for a genuine type relationship, composition is the better design.
- Always use
@Overrideon every overridden method. The compiler catches signature typos. Without it, a misspelled lifecycle method compiles but never runs, producing mysterious "my robot does nothing" bugs. - Subsystems extend
SubsystemBase, not hardware classes. AnIntakeSubsystem extends SubsystemBaseis correct. AnIntakeSubsystem extends TalonFXis a design error that exposes 200+ motor controller methods to every caller. - Call
super()in every subclass constructor. ForSubsystemBasesubclasses, forgettingsuper()means the subsystem never registers with the Command Scheduler and your commands will fail silently. - Don't extend a class just to reuse code. If the subclass isn't genuinely a more specific version of the superclass, use composition instead. Inheritance that's only for code reuse creates fragile coupling.
Knowledge Check
public void teleopPeriodoc() { drive.update(); } without @Override. The robot runs but the drive doesn't move. What happened?IntakeSubsystem extends a MotorSubsystem superclass. The superclass constructor requires a CAN ID. What must the first line of IntakeSubsystem's constructor be?DriveSubsystem extend TalonFX to reuse TalonFX's methods? Why or why not?Build a Subsystem Hierarchy
- Create a
MotorSubsystembase class with aprotected final TalonFX motor, a parameterized constructor, astop()method, and agetOutput()getter. This class should work as a general single-motor subsystem. - Create
IntakeSubsystem extends MotorSubsystem. Callsuper()in the constructor. Add a beam break sensor and arun()method. Overridestop()to also reset agamePieceHeldflag, callingsuper.stop()to ensure the motor actually stops. - Verify
@Overridecatches errors: intentionally misspellstop()in the subclass asstopp()with@Overridepresent. Confirm the compiler rejects it. Then fix the spelling. - Instantiate both classes. Confirm that
IntakeSubsystemhas access to the inheritedgetOutput()method without redefining it. - Bonus: Find
SubsystemBasein WPILib's source code on GitHub. What does its constructor do? What protected or public methods does it provide to your subsystem? How does this compare to theMotorSubsystemyou built?