Classes and Objects
You have been using objects since Unit 2. Every TalonFX, every XboxController, every SwerveModule is an object — an instance of a class. This lesson explains what that means, why Java is built around this model, and how to define your own classes for robot subsystems.
By the end of this lesson, you will:
- Define the relationship between a class (blueprint) and an object (instance)
- Identify the four members a class can contain: fields, constructors, methods, and nested types
- Declare a simple class with fields and methods and instantiate it with
new - Explain what an object reference variable holds and why two reference variables can point to the same object
- Recognize the class structure of a WPILib subsystem and map it to the concepts in this lesson
Everything Is an Object
Before you wrote a single class yourself, you were already working inside an object-oriented system. new TalonFX(0) calls the TalonFX class constructor and creates one motor controller object. new XboxController(0) creates a joystick object. driverController.getLeftY() calls a method on that object. The entire WPILib framework is a collection of classes — and your Robot.java file defines a class that extends one of them.
A class is a blueprint — a definition of what a thing is, what data it holds, and what it can do. An object is a live instance of that blueprint in memory: real data, real state, real behavior. Java is object-oriented because almost everything is modeled as an object with state and behavior bundled together.
The Four Members of a Class
Every Java class can contain four kinds of members. These are the same building blocks you'll find in every WPILib class, every vendor library, and every subsystem you write this season. Click each card to see how it appears in FRC code.
👇 Click each card to see the FRC contextData the object holds. In a SwerveModule class: the drive motor, steer motor, encoder, and current angle are all fields. Each object instance gets its own copy.
Special methods that run when new is called. new TalonFX(0) calls the TalonFX constructor with CAN ID 0. Covered fully in Lesson 2.
Actions the object can perform or calculations it can return. motor.set(0.5), encoder.getPosition(), and isAtTarget() are all method calls on objects.
public, private, protected — control who can see a field or method. private hides the motor object inside the subsystem; public exposes the speed-setting method. Covered fully in Lesson 4.
Defining a Class
Here is the minimal structure of a Java class. Every subsystem you write this season will follow this skeleton:
Instantiating Objects: the new Keyword
A class definition does nothing on its own — it's a blueprint sitting on a shelf. The new keyword activates that blueprint, allocates memory, runs the constructor, and hands back a reference to the live object. Each call to new creates an independent object with its own copy of every instance field.
Use the Object Factory below to see this in action. Each click instantiates a new SwerveModule with different CAN IDs — each gets its own driveMotor, steerMotor, and steerEncoder.
private final TalonFX driveMotor;
private final TalonFX steerMotor;
private final CANcoder encoder;
private double angleDeg = 0.0;
// ... constructor + methods
}
Object references
When you write SwerveModule fl = new SwerveModule(0, 1, 2);, the variable fl doesn't hold the entire object — it holds a reference to the object in memory. This has one important consequence: if you assign the same object to two variables, both variables point to the same object. Changing the object through one variable changes it for the other.
A WPILib Subsystem Is Just a Class
Everything you've seen in WPILib is built on this model. TimedRobot is a class. Your Robot.java extends it. When WPILib's main loop calls teleopPeriodic(), it's calling a method on your Robot object. In Unit 6 you'll write SubsystemBase subclasses — each one is a class that models one mechanism on the robot, with fields for that mechanism's hardware and methods for its behaviors.
The class definition lives in a .java file. It describes what every instance of this class will look like — fields, constructor, methods. Writing the class doesn't create any objects.
new IntakeSubsystem() runs the constructor — allocates memory, creates the TalonFX and DigitalInput objects inside it, and hands back a reference. This typically happens in robotInit() or in a containing class.
Methods on the object are called through the reference variable using dot notation. The intake variable knows which object it points to; Java routes the method call to that specific object's state and behavior.
During robot inspections, I look for teams that have a single monolithic Robot.java file with hundreds of lines of teleop logic versus teams that have a clean Robot.java that instantiates and delegates to subsystem classes. The second pattern doesn't just look better — it's faster to debug, easier to hand to a new programmer, and produces fewer mysterious interactions between mechanisms. The class boundary forces the programmer to think about what belongs to what. That clarity shows up on the competition field.
🔌 System Check
- One class per mechanism. Each physical subsystem — drivetrain, intake, arm, climber — should be its own class. The class boundary defines what code belongs to that mechanism and nothing else.
- Hardware objects are instance fields. Motor controllers, encoders, and sensors live as
private finalfields of their subsystem class. They should not be accessible from outside the class. - One instance of each subsystem. The robot has one physical intake, so there should be one
IntakeSubsystemobject. Creating multiple instances of a subsystem means multiple objects trying to control the same hardware. - Fields initialized in the constructor. Hardware objects that require CAN IDs or port numbers should be created in the constructor, not as field initializers, so the configuration is explicit and parameterizable.
Knowledge Check
TalonFX motor = new TalonFX(5);. What does the variable motor hold?SwerveModule objects: fl (front-left) and fr (front-right). A programmer runs fr = fl;. What happens next time they call fr.setDesiredState(…)?TalonFX class and the four TalonFX objects in a swerve drivetrain?Design and Build Your First Subsystem Class
- Without writing code yet: list the fields, constructor parameters, and public methods you would expect a
ClimberSubsystemclass to have. Think physically — what does the climber need to remember? What can it do? - Write the class in WPILib VS Code. Give it at least two
private finalhardware fields, a constructor that initializes them with CAN IDs fromConstants.java, and three public methods:extend(),retract(), andisAtLimit(). - In a separate file (or your existing
Robot.java), instantiate oneClimberSubsystemas a field, and call its methods fromteleopPeriodic()based on button inputs. - Try instantiating two
ClimberSubsystemobjects with the same CAN IDs. What happens when you deploy? Why? - Bonus: Look at Team 2910's most recent public robot repository. Find two subsystem classes. For each one, list: the instance fields, the constructor parameters, and the public methods. How does their structure compare to the pattern in this lesson?