Abstract Classes and Interfaces
Abstract classes and interfaces are both ways of defining a contract — a set of methods a class must provide — without implementing every method themselves. WPILib uses both extensively: SubsystemBase is an abstract class, Sendable is an interface. Knowing when to use each is the practical skill.
By the end of this lesson, you will:
- Write an abstract class with abstract methods that subclasses must implement
- Define an interface and implement it in a concrete class
- Explain the key differences: abstract classes have state and partial implementation; interfaces are pure contracts (with optional defaults)
- Implement common WPILib interfaces:
Sendable,Supplier,Consumer - Choose between abstract class and interface for a given FRC design problem
The Problem They Both Solve
Inheritance from Lesson 5 lets you share concrete implementations. But sometimes you want to define a contract — "every class that claims to be one of these must provide this method" — without dictating how. You want the type guarantee without forcing a specific implementation.
Abstract classes and interfaces both solve this, but with different constraints. The quick mental model: an abstract class is a partially-built class; an interface is a to-do list.
Abstract Classes
An abstract class is a class that cannot be instantiated directly — you can only instantiate its concrete subclasses. It can contain a mix of concrete methods (with implementation) and abstract methods (with no body — subclasses must provide it). Abstract classes can have fields, constructors, and any access modifier.
Interfaces
An interface is a pure contract — a list of methods a class promises to provide. A class can implement multiple interfaces, while it can only extend one class. Interface methods are implicitly public abstract unless marked default (which provides a fallback implementation).
WPILib relies heavily on functional interfaces — interfaces with a single abstract method — for callbacks and suppliers. You'll use these constantly once you reach Command-Based programming.
Abstract Class vs. Interface: The Decision
Both tools define contracts. The choice between them comes down to whether you need shared state, shared implementation, or multiple inheritance. Select any scenario to see which tool fits and why.
Use when subclasses share state and partial implementation.
- Can have instance fields and constructors
- Can mix abstract and concrete methods
- A class can only extend one
- Models an is-a relationship with shared scaffolding
- FRC:
SubsystemBase,CommandBase, yourMotorSubsystem
Use when you need a capability contract across unrelated classes.
- No instance fields (constants only)
- All methods public (abstract by default)
- A class can implement many
- Models a can-do relationship
- FRC:
Sendable,Supplier,Consumer,BooleanSupplier
TalonFX field and a stop() implementation, but each must provide its own periodic() logic
▶
Command that accepts "any callable that takes no args and returns nothing"
▶
- Use abstract classes when subclasses share state. If every subsystem variant needs a
TalonFX, a current limit check, and astop()method, an abstract class centralizes that. Subclasses provide the mechanism-specific behavior through abstract method implementations. - Use interfaces for capabilities that cross class hierarchies.
Sendableis implemented by motors, sensors, subsystems, and controllers — objects with nothing else in common. The interface defines only what they can do, not what they are. - Functional interfaces (
Supplier,Consumer,Runnable) enable lambda expressions. Any method that accepts aBooleanSuppliercan receive a lambda (() -> sensor.get()). This is the foundation of Command-Based trigger bindings. - Never instantiate an abstract class directly. If a class is abstract, you need a concrete subclass. The compiler enforces this —
new MotorSubsystem(5)is a compile error ifMotorSubsystemis abstract.
Knowledge Check
public abstract class BaseAuto { public abstract void run(); }. You write new BaseAuto(). What happens?BooleanSupplier is a functional interface with one method: boolean getAsBoolean(). A method accepts a BooleanSupplier. Which can you pass?Design a Subsystem Contract
- Convert your
MotorSubsystemfrom Lesson 5 to an abstract class. Makeperiodic()andgetOutput()abstract. Verify the compiler rejects a direct instantiation. - Create a
Loggableinterface with a singlevoid log()method. Implement it inIntakeSubsystemandClimberSubsystem. Each implementation should callSmartDashboard.putNumber()with subsystem-specific data. - Write a
logAll(Loggable[] loggables)static method in a utility class. Pass it an array containing your two subsystems and verify each one logs its own data. - Use a lambda to create a
BooleanSupplierthat returnstruewhen the intake has a game piece. Pass it to a method that acceptsBooleanSupplierand prints its result every second. - Bonus: Find WPILib's
Sendableinterface. What is its single abstract method? What does implementing it allow your subsystem to do? How doesSubsystemBasealready implementSendableon your behalf?