Unit 4 · Lesson 3

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.java class using static final fields
  • 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.

Static vs. Instance Field Visualizer
SwerveModule class
static moduleCount0
instance fields:
driveId, angleDeg
(per-object)
— not yet created —
— not yet created —
Click "new SwerveModule(…)" to create instances. Watch the static field update for all.

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).

// Constants.java — a class that exists purely to hold static final values public class Constants { // CAN IDs public static final int DRIVE_FL_ID = 0; public static final int DRIVE_FR_ID = 3; public static final int INTAKE_ID = 8; // Physical constants public static final double WHEEL_DIAMETER_M = 0.1016; // 4 inches public static final double MAX_DRIVE_SPEED_MPS = 4.5; // Current limits public static final int DRIVE_CURRENT_LIMIT = 50; public static final int STEER_CURRENT_LIMIT = 30; // Called as Constants.DRIVE_FL_ID — never new Constants() }

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.

// ⚠️ Mutable static — shared state, use only when truly class-wide public class SwerveModule { private static int moduleCount = 0; // counts all instantiated modules public SwerveModule(int driveId, int steerId, int encoderId) { // ... initialize fields ... moduleCount++; // every new instance increments the shared counter } public static int getModuleCount() { return moduleCount; } } // After creating four modules: SwerveModule.getModuleCount(); // returns 4

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.

// Pure transformations — no instance needed, natural candidates for static public static double clamp(double value, double min, double max) { return Math.max(min, Math.min(max, value)); } public static double ticksToMeters(double ticks, double ticksPerRev, double wheelCircumferenceM) { return (ticks / ticksPerRev) * wheelCircumferenceM; } public static double applyDeadband(double input, double threshold) { return Math.abs(input) < threshold ? 0.0 : input; } // Called on the class, not an instance double output = DriveUtils.clamp(joystickInput, -1.0, 1.0); double speed = DriveUtils.applyDeadband(rawY, 0.05);

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.

// ❌ Wrong — uses instance field, cannot be static public static void run() { intakeMotor.set(0.6); // compile error: cannot access instance field from static method } // ✅ Correct — instance method accesses instance state naturally public void run() { intakeMotor.set(INTAKE_SPEED); // "this.intakeMotor" is implicit } // ❌ Calling a static method through an instance variable is legal but misleading SwerveModule fl = new SwerveModule(0, 1, 2); int count = fl.getModuleCount(); // compiles but looks like an instance call // ✅ Call static methods on the class name — makes the static nature obvious int count = SwerveModule.getModuleCount();
💡 The three legitimate uses of static in FRC code

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

⚙️ Static Members in Competition Code
  • All hardware configuration values in Constants.java as public 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 on fl'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 DriveUtils or MathUtils class, 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

A class has 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?
  • 13 — only instance a's copy
  • 22 — only instance b's copy
  • 35 — the static field is shared across all instances; increments from any instance accumulate in the single class-level copy
  • 40 — static fields can only be modified through the class name, not through instance variables
Why can't a static method access instance fields directly?
  • 1It's a Java security restriction to prevent unauthorized field access
  • 2Static methods run before any instances are created, so instance fields don't exist yet
  • 3A static method has no this reference — it doesn't belong to any specific object, so Java has no way to know which object's instance field to read. Instance fields are per-object; a method with no object has no instance to read from.
  • 4Static methods can only be in classes that have no instance fields
You have a method 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?
  • 1Instance method — all subsystem methods should be instance methods for consistency
  • 2Static — it has no dependency on any object's state; it transforms its inputs using only parameters and class-level constants; making it static communicates "this is a pure computation, not a stateful operation"
  • 3Neither — it should be moved to Constants.java as a field
  • 4It depends on whether the class has other instance methods
💪 Practice Prompt

Build and Audit a Constants Class

  1. Create a Constants.java file for your practice robot project. Organize it with inner classes (or block comments) for each subsystem: DriveConstants, IntakeConstants, ArmConstants. Populate each with at least four public static final fields covering CAN IDs, current limits, and speed values.
  2. Search your existing robot code for any numeric literal (magic number) inside a periodic method or subsystem. Move each one to Constants.java as a named constant and replace the literal with the constant reference.
  3. Write a DriveUtils class with three public static methods: clamp(double, double, double), applyDeadband(double, double), and ticksToMeters(double). Verify none of them require an instance to call — test by calling them as DriveUtils.clamp(…) without creating a DriveUtils object.
  4. Add a private static int instanceCount field to one of your subsystem classes. Increment it in the constructor. After instantiating one object in robotInit(), print the count to the console. Verify it equals 1.
  5. 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.