Static Members
You've used static members since Unit 2 — Math.abs(), static final constants, and SmartDashboard.putNumber() are all static. This lesson explains what static means at the memory level, when to reach for it in robot code, and when instance members are the right choice instead.
By the end of this lesson, you will:
- Explain the difference between static members (belonging to the class) and instance members (belonging to each object)
- Identify when a field or method should be static vs. instance-level
- Write a correct
Constants.javaclass usingstatic finalfields - Call static methods using the class name and explain why calling them on an instance variable is misleading
- Recognize the three legitimate uses of static in FRC code: constants, pure utility methods, and instance counters
The Class vs. the Object
Every member of a class belongs to one of two scopes. Instance members belong to each individual object — each SwerveModule has its own driveMotor field, independent of all other modules. Static members belong to the class itself — there is one copy, shared by all instances, accessible without creating any object at all.
The keyword static promotes a member from "per-object" to "per-class." The consequences flow from this single distinction.
Static vs. Instance: Live Visualizer
The visualizer below shows a SwerveModule class with one static field (moduleCount) and two instance fields (driveId, angleDeg). Click the button to create new instances and observe how the static field is shared while instance fields are independent.
driveId, angleDeg
(per-object)
Static Fields
Constants: the static final pattern
The most common use of static fields in FRC is the static final constant pattern — a field that belongs to the class and never changes. This is what all of Constants.java is built from. Static makes it class-level (no object needed). Final makes it immutable (cannot be reassigned).
Mutable static fields: use with extreme care
A static field without final is shared and mutable — any code that changes it changes it for every instance simultaneously. In robot code, this is almost always a design error. The only valid FRC use is an instance counter or a class-wide flag that legitimately should reflect the aggregate state of all instances.
Static Methods
A static method belongs to the class and can be called without any instance. It cannot access instance fields or call instance methods — it only has access to other static members, its own parameters, and local variables.
A method that only transforms its inputs to an output — no object state, no side effects — is a natural candidate for static. This communicates clearly: "this method has no dependency on any object's state." WPILib's MathUtil class is entirely static utility methods.
The signal to not use static on a method is when it needs to read or write any instance field. A method that uses this.motor or this.currentAngle is inherently about a specific object's state — making it static would require passing the object as a parameter, which defeats the purpose of object-oriented design.
1. Constants (static final). Every value in Constants.java. Hardware IDs, speeds, limits, dimensions. No instances needed, never change.
2. Pure utility methods. Math transformations, unit conversions, deadband application. Input → output, no state. Belong in a utility class (or WPILib's MathUtil).
3. Class-wide counters or flags. Rarely needed. Tracking the total number of instantiated subsystems, or a global fault flag that any code path can set. Use sparingly — shared mutable state is a source of subtle bugs.
🔌 System Check
- All hardware configuration values in
Constants.javaaspublic static final. CAN IDs, current limits, speeds, and port numbers appear in one place and one place only. A duplicated constant is a constant waiting to drift out of sync. - Never call a static method on an instance variable.
SwerveModule.getModuleCount()is correct.fl.getModuleCount()is misleading — it looks like the result depends onfl's state, but it doesn't. Use the class name. - Static utility methods belong in utility classes, not subsystem classes. Math transformations that aren't specific to one subsystem — deadband, clamping, angle wrapping — belong in a
DriveUtilsorMathUtilsclass, not scattered across subsystems where they're hard to find and easy to duplicate. - Mutable static fields create invisible coupling. Any code that reads a mutable static field depends on every code path that writes it. In a robot program with 10 subsystems and 50 periodic methods, this coupling is nearly impossible to reason about. Prefer passing state as parameters or through objects.
Knowledge Check
private static int callCount = 0;. Two instances (a and b) are created. Code increments callCount through a three times and through b twice. What is callCount?convertTicksToMeters(double ticks) that uses only its parameter and the constant WHEEL_CIRCUMFERENCE_M (already a static final in Constants.java). Should this method be static or an instance method?Build and Audit a Constants Class
- Create a
Constants.javafile for your practice robot project. Organize it with inner classes (or block comments) for each subsystem:DriveConstants,IntakeConstants,ArmConstants. Populate each with at least fourpublic static finalfields covering CAN IDs, current limits, and speed values. - Search your existing robot code for any numeric literal (magic number) inside a periodic method or subsystem. Move each one to
Constants.javaas a named constant and replace the literal with the constant reference. - Write a
DriveUtilsclass with threepublic staticmethods:clamp(double, double, double),applyDeadband(double, double), andticksToMeters(double). Verify none of them require an instance to call — test by calling them asDriveUtils.clamp(…)without creating aDriveUtilsobject. - Add a
private static int instanceCountfield to one of your subsystem classes. Increment it in the constructor. After instantiating one object inrobotInit(), print the count to the console. Verify it equals 1. - Bonus: Look at how Team 2910 structures their
Constants.java(or equivalent) in their public repository. How do they organize it? Do they use nested classes, inner interfaces, or flat organization? Compare their approach to yours and write a paragraph explaining which you'd adopt and why.