Unit 4 · Lesson 2

Constructors

A constructor is the code that runs the moment an object is born. Every new TalonFX(0), every new SwerveModule(…), every new IntakeSubsystem() calls a constructor. Understanding constructors means understanding exactly what state an object starts in — and why startup order matters on a robot.

By the end of this lesson, you will:

  • Write a constructor with the correct syntax — matching class name, no return type
  • Distinguish between a default constructor and a parameterized constructor
  • Use this() to chain constructors and avoid duplicating initialization logic
  • Use this.fieldName to disambiguate when a parameter name shadows a field name
  • Explain why hardware initialization belongs in the constructor, not in a separate init() method

What a Constructor Is

A constructor is a special method that runs automatically when new ClassName(…) is called. Its job is to put the object into a valid, usable initial state — assigning fields their starting values, creating the hardware objects the subsystem needs, and performing any one-time configuration. A constructor can never be called manually after the object exists; it runs exactly once, at birth.

Three things make constructors syntactically different from regular methods: they have the same name as the class, they have no return type (not even void), and they are called only through new.

Constructor Anatomy

Click each part of the constructor below to understand what it does.

Constructor Anatomy Explorer
publicaccess   IntakeSubsystemclass name ( int motorId, int sensorPortparameters ) { this.intakeMotor = new TalonFX(motorId)body ;  }
← click any part to learn what it means

Three Constructor Patterns

A default constructor takes no parameters. If you don't write any constructor at all, Java generates an invisible no-argument constructor that sets all fields to their defaults (0, false, null). As soon as you write any constructor yourself, Java's automatic one disappears.

For subsystems where all configuration lives in Constants.java, a no-argument constructor is appropriate — the constructor reads the constants directly rather than receiving them as parameters.

public class IntakeSubsystem { private final TalonFX motor; private final DigitalInput beamBreak; // No-argument constructor — reads from Constants public IntakeSubsystem() { motor = new TalonFX(Constants.INTAKE_MOTOR_ID); beamBreak = new DigitalInput(Constants.BEAM_BREAK_PORT); } } // Called with no arguments IntakeSubsystem intake = new IntakeSubsystem();

A parameterized constructor accepts arguments that customize the object at creation time. This makes the class reusable — the same SwerveModule class can represent any corner of the drivetrain because the CAN IDs are passed in rather than hardcoded.

When a parameter name matches a field name, use this.fieldName to refer to the field. this is a reference to the current object.

public class SwerveModule { private final TalonFX driveMotor; private final TalonFX steerMotor; private final CANcoder encoder; // Parameter names match field names — use "this." to distinguish public SwerveModule(int driveId, int steerId, int encoderId) { this.driveMotor = new TalonFX(driveId); this.steerMotor = new TalonFX(steerId); this.encoder = new CANcoder(encoderId); } } // Each corner passes its own CAN IDs — one class, four unique instances SwerveModule fl = new SwerveModule(0, 1, 2); SwerveModule fr = new SwerveModule(3, 4, 5);

Constructor chaining lets one constructor call another using this(…) as the first statement. This avoids duplicating initialization logic when you want multiple constructors — a "convenience" constructor delegates to the full one.

This is the same delegation pattern from Unit 2 Lesson 6's method overloading — the shorter form calls the full form, keeping the logic in exactly one place.

public class SwerveModule { private final TalonFX driveMotor; private final TalonFX steerMotor; private final CANcoder encoder; private final boolean invertDrive; // Full constructor — all the real logic lives here public SwerveModule(int driveId, int steerId, int encoderId, boolean invertDrive) { this.driveMotor = new TalonFX(driveId); this.steerMotor = new TalonFX(steerId); this.encoder = new CANcoder(encoderId); this.invertDrive = invertDrive; configureMotors(); } // Convenience overload — calls the full constructor, defaulting invert to false public SwerveModule(int driveId, int steerId, int encoderId) { this(driveId, steerId, encoderId, false); // must be FIRST statement } private void configureMotors() { /* apply current limits, neutral mode, etc. */ } } // Convenience call — right-side modules are not inverted SwerveModule fl = new SwerveModule(0, 1, 2); // Full call — left-side modules are inverted SwerveModule fr = new SwerveModule(3, 4, 5, true);

Hardware Initialization in Constructors

A common pattern in beginner FRC code is a separate init() method that the programmer calls after construction. This creates a window where the object exists but isn't ready to use — a partially-constructed object that will throw a NullPointerException if anyone calls a method before calling init().

// ❌ Two-phase construction — dangerous gap between new and init IntakeSubsystem intake = new IntakeSubsystem(); // intake.motor is null here — calling intake.run() now crashes intake.init(); // easy to forget, wrong order, or called twice // ✅ Single-phase construction — object is ready immediately after new IntakeSubsystem intake = new IntakeSubsystem(); // intake.motor exists right now — safe to call intake.run() immediately

The rule: when new returns, the object must be in a fully valid state. Every field that will ever be used must be initialized in the constructor. This guarantee is what makes method calls on the object safe without checking for null first.

🔍 LRI Observation

The most common constructor-related bug I see in code review is a private TalonFX motor; field with no initializer and no constructor assignment, combined with methods that call motor.set(…). The code compiles. The robot enables. The first button press throws a NullPointerException that disables the roboRIO. The Driver Station shows a mysterious disconnect. Teams spend hours checking wiring before someone looks at the stack trace. Fully initializing objects in the constructor prevents this entire class of bug.

The this Keyword

Inside a constructor or method, this is a reference to the current object — the specific instance the code is running on. It has two uses that appear constantly in FRC code:

// Use 1: disambiguate when a parameter shadows a field name public SwerveModule(int driveId) { this.driveId = driveId; // this.driveId = field; driveId = parameter } // Use 2: call another constructor (must be FIRST line) public SwerveModule(int driveId) { this(driveId, DEFAULT_STEER_ID, DEFAULT_ENCODER_ID); } // Common mistake: assigning the wrong direction public SwerveModule(int driveId) { driveId = this.driveId; // ❌ backwards — assigns the uninitialized field TO the param }
💡 Prefer distinctive parameter names to avoid the this. pattern

Some teams avoid the this.field = param ambiguity entirely by naming parameters differently: int driveMotorId as the parameter, driveId as the field. This makes assignment unambiguous: driveId = driveMotorId;. Both approaches are valid — the important thing is that the direction is always field ← parameter, never the reverse.

🔌 System Check

⚙️ Constructor Checklist for Robot Code
  • Every hardware field must be assigned in the constructor. If a field is declared but never assigned in the constructor, calling any method that uses it throws NullPointerException at runtime. There are no compiler warnings for this.
  • No two-phase construction. Avoid init() methods that must be called after new. Put all initialization in the constructor. If setup is complex, call private helper methods from the constructor — but complete it before the constructor returns.
  • Hardware constructors in robotInit() or subsystem constructors, never in periodic. Calling new TalonFX(0) inside teleopPeriodic() creates a new object every 20ms. The old object loses its configuration and the CAN bus floods with repeated initialization messages.
  • When chaining constructors, this() must be the first statement. The Java compiler enforces this. Putting anything before this() in a constructor is a compile error.

Knowledge Check

A class declares private TalonFX motor; as a field but never assigns it in any constructor. What happens at runtime when a method calls motor.set(0.5)?
  • 1Java automatically creates a default TalonFX on CAN ID 0
  • 2A NullPointerException is thrown — motor is null and calling a method on null crashes the program
  • 3The line is silently ignored — Java skips method calls on null fields
  • 4A compile error — unassigned object fields are caught at compile time
A constructor contains public SwerveModule(int driveId) { this(driveId, 1, 2); motor.set(0); }. What is wrong with this?
  • 1You cannot call motor.set() inside a constructor
  • 2The this() call is missing a closing semicolon
  • 3Nothing syntactically — but calling motor.set(0) in a constructor is likely wrong: the full constructor may not have initialized motor yet when this line runs, and setting a motor to 0 on construction is unnecessary since motors default to stopped
  • 4this() must be the last statement in a constructor, not the first
Why does a parameterized SwerveModule constructor (accepting drive, steer, and encoder IDs) produce better code than four separate classes — FrontLeftModule, FrontRightModule, etc. — each with hardcoded IDs?
  • 1Parameterized constructors are faster at runtime than separate classes
  • 2One class with parameterized construction means bug fixes and improvements apply to every module simultaneously; four separate classes means maintaining four copies of the same logic — and if the team changes CAN IDs, they edit four places instead of one
  • 3Java only allows four classes per project
  • 4No practical difference — both patterns produce the same robot behavior
💪 Practice Prompt

Build a Fully Initialized Subsystem

  1. Take the ClimberSubsystem from Lesson 1's practice prompt. If it has any fields that aren't initialized in the constructor, fix that now. Verify: after new ClimberSubsystem() returns, every field should be non-null and the object should be immediately usable.
  2. Add a second constructor that accepts the CAN IDs as parameters, allowing the class to be instantiated with either default IDs (from Constants) or custom IDs. Use this() constructor chaining so the initialization logic lives in exactly one place.
  3. Introduce a deliberate bug: remove the motor initialization from the constructor but leave the method that calls motor.set(). Run the code and record the exact exception message. Then fix the bug and confirm the exception disappears.
  4. Add a private configureMotor() helper method that applies current limits and neutral mode to the motor. Call it from the constructor. Verify the constructor is still single-phase: the object is fully ready when new returns.
  5. Bonus: Look at WPILib's source code on GitHub. Find the constructor for edu.wpi.first.wpilibj.XboxController. What parameters does it take? What does it initialize? Does it call any superclass constructor with super()? (Lesson 5 covers super().)