Unit 2 · Lesson 3

Operators

Operators are the verbs of Java — they act on your variables to produce results. On a robot, those results become motor outputs, enable conditions, safety gates, and state transitions. Getting operators wrong doesn't usually crash the program. It just makes the robot do the wrong thing, and figuring out why is the hard part.

By the end of this lesson, you will:

  • Use arithmetic, assignment, comparison, logical, and unary operators correctly in robot code
  • Explain short-circuit evaluation and why && and || are preferred over & and | for boolean logic
  • Predict the result of an expression involving mixed types, operator precedence, and compound assignment
  • Identify the = vs == bug by sight and explain exactly what each one does
  • Apply % (modulo) to real robot problems — wrapping angles, cycling through modes

Operators as Robot Decisions

Every useful thing your robot code does involves an operator. Scaling joystick input to a motor: arithmetic. Checking whether a limit switch is triggered: comparison. Enabling a mechanism only when the right button is held and the arm is in range: logical. Accumulating distance traveled: compound assignment.

Operators fall into five categories. Use the explorer below to learn each one in context — not in the abstract, but in terms of the robot calculation it produces.

← select an operator above to see its behavior and a robot example

Compound Assignment in the Robot Loop

Compound assignment operators (+=, -=, *=, /=, %=) combine an arithmetic operation with assignment. They are syntactic shorthand, but they show up constantly in robot code for a reason: the robot loop runs 50 times per second, and many values are updated incrementally on each cycle.

// Accumulating distance traveled in inches double totalDistanceIn = 0.0; // In periodic() — adds this cycle's increment to the running total totalDistanceIn += driveEncoder.getVelocity() * 0.02; // velocity * 20ms dt // ────── equivalent to, but cleaner than ────── totalDistanceIn = totalDistanceIn + (driveEncoder.getVelocity() * 0.02);
// Ramp rate: smoothly approach a target output over multiple cycles // Without ramp rate, instantaneous full power can snap a chain double currentOutput = 0.0; double targetOutput = 1.0; static final double RAMP_STEP = 0.05; // 5% per loop cycle // In periodic() if (currentOutput < targetOutput) { currentOutput += RAMP_STEP; // increment toward target } currentOutput = Math.min(currentOutput, targetOutput); // don't overshoot driveMotor.set(currentOutput);
💡 Ramp rate: where software protects hardware

This is one of the clearest examples of the mechanical–software relationship in this course. When a motor goes from 0 to full speed instantaneously, the mechanical shock can strip a gear, snap a chain, or bend a shaft. A software ramp rate — a few lines of += in the periodic loop — absorbs that shock in code before it reaches the hardware. Many vendor motor controllers have built-in ramp rate settings, but understanding how to implement it manually is the foundation for understanding theirs.

Modulo: The Operator FRC Programmers Underuse

The % operator returns the remainder after integer division. It doesn't appear in many beginner examples, but it shows up in robot code more than you'd expect — particularly anywhere the robot needs to wrap a value within a range or cycle through a fixed set of options.

// Basic: remainder after division int remainder = 10 % 3; // 10 ÷ 3 = 3 remainder 1 → result is 1 int evenCheck = 8 % 2; // 8 ÷ 2 = 4 remainder 0 → result is 0 (even)

FRC Use 1 — Cycling through autonomous modes

// Cycle through 4 auto options with a single button press // Without modulo: if (autoMode == 3) autoMode = 0; else autoMode++; // With modulo — cleaner, scales to any number of modes private int autoMode = 0; static final int NUM_AUTO_MODES = 4; if (driverController.getAButtonPressed()) { autoMode = (autoMode + 1) % NUM_AUTO_MODES; // cycles: 0 → 1 → 2 → 3 → 0 → 1 → ... indefinitely }

FRC Use 2 — Wrapping angles to stay within [0, 360)

// Encoder accumulates past 360° — wrap it back into range // Note: Java's % with doubles preserves sign, so we add 360 first double rawAngle = -45.0; // encoder reading, could be any value double wrappedAngle = ((rawAngle % 360.0) + 360.0) % 360.0; // -45 → 315.0 — the correct equivalent positive angle // Why the double modulo? Java's % can return negative values for // negative inputs. Adding 360 then taking % again guarantees positive.
🔍 LRI Observation

Angle wrapping is one of the most common sources of swerve drive bugs I see during inspection and at events. Teams use a heading controller to keep the robot pointed in a direction, the raw encoder value crosses 0° or 360°, and the controller suddenly sees a 359° error instead of a 1° error — causing the robot to spin a full rotation instead of making a tiny correction. The fix is always some form of angle wrapping, and modulo is typically at the heart of it. Unit 7 covers this in full for swerve, but the operator is here.

Logical Operators and Short-Circuit Evaluation

Logical operators combine boolean expressions. On a robot, they are your safety gates and enable conditions. "Run the shooter only if the button is held and the intake is clear" is a logical AND. "Trigger a fault if the current is too high or the temperature exceeds the limit" is a logical OR.

Interactive truth table

Toggle A and B to see how &&, ||, and ! evaluate for every combination.

Logic Table — toggle inputs to see results
Try all four combinations
A && B false true only if both A and B are true
A || B false true if at least one of A or B is true
!A true inverts A — true becomes false, false becomes true

Short-circuit evaluation

Java's && and || are short-circuit operators: they stop evaluating as soon as the result is certain. For &&, if the left side is false, the right side is never evaluated — the whole expression is already false. For ||, if the left side is true, the right side is skipped.

This matters in robot code whenever the right-hand condition could throw an error or produce a side effect if evaluated on a null or uninitialized object.

// Safe pattern: check for null before calling a method // If armEncoder is null, the second condition is never evaluated // — no NullPointerException if (armEncoder != null && armEncoder.getPosition() > 90.0) { // safe to use the encoder } // Fault detection — stop checking after first fault is found boolean hasFault = motorOverTemp() || driveCurrentTooHigh() || canBusError(); // If motorOverTemp() is true, the other two methods are never called
💡 && vs. & — and when it actually matters

The single & (bitwise AND) and | (bitwise OR) evaluate both sides regardless of the result — no short-circuit. For boolean expressions in if statements, you almost always want && and ||. The single-character versions are for bitwise operations on integers (like manipulating CAN bus message fields or LED control registers). Using & where you mean && in a safety condition is a subtle bug that only appears when the right-hand expression has a side effect — like incrementing a counter or calling a method that throws on a bad sensor state.

The Most Expensive Typo in FRC: = vs ==

Assignment (=) stores a value. Comparison (==) checks whether two values are equal. They look nearly identical and do completely different things. Using one where you meant the other is one of the most common bugs across all experience levels, and in some cases the Java compiler will catch it — but not always.

// ❌ Assignment instead of comparison — assigns true, condition always runs if (intakeRunning = true) { // ALWAYS true — this is an assignment intakeMotor.set(INTAKE_SPEED); } // ✅ Comparison — checks whether intakeRunning is currently true if (intakeRunning == true) { intakeMotor.set(INTAKE_SPEED); } // ✅✅ Better — for booleans, the variable IS the condition if (intakeRunning) { intakeMotor.set(INTAKE_SPEED); } // ✅✅ And its inverse if (!intakeRunning) { intakeMotor.set(0.0); }

Java will warn you if you write if (someBoolean = true) — the compiler knows that assignment inside a condition is probably a mistake. But if (someInt = 5) will not compile at all because an int assignment doesn't produce a boolean. The dangerous case is specifically a boolean on the left side, because a boolean assignment does produce a valid condition.

Operator Precedence

When an expression has multiple operators, Java evaluates them in a fixed order — higher-precedence operators run first. The order roughly mirrors standard math: multiplication before addition, for example. The full precedence table is long, but knowing the key tiers is enough to write robot code without surprises.

Highest ++ -- ! Unary / NOT
2 * / % Multiply / Divide
3 + - Add / Subtract
4 < > <= >= Comparison
5 == != Equality
6 && Logical AND
Lowest || Logical OR

The precedence rule that catches robot programmers most often: && binds tighter than ||. A mixed condition reads differently than it looks.

// Intent: enable shooter IF (button held AND arm in range) OR (auto mode active) // Written without parentheses: if (shootButton && armInRange || autoActive) { // ⚠️ parsed as (shootButton && armInRange) || autoActive // This is actually correct here — && binds tighter than || // BUT: easy to mis-read, and if someone adds more conditions, intent breaks fast } // ✅ Use parentheses to make intent explicit — no ambiguity, no surprises if ((shootButton && armInRange) || autoActive) { shooter.enable(); } // ⚠️ A real precedence bug — looks like it checks (a OR b) AND c // Actually evaluates as a OR (b AND c) because && binds tighter if (currentTooHigh || tempTooHigh && motorEnabled) { // Not what most programmers expect at a glance triggerFault(); } // ✅ Explicit and unambiguous if ((currentTooHigh || tempTooHigh) && motorEnabled) { triggerFault(); }

The rule of thumb used on 2910: if a condition has more than two operands, add parentheses. The compiler doesn't care. Your teammates at midnight before a regional will.

🔍 Event Observation

A team had a fault detection condition that was supposed to cut motor power when either of two sensors read out of range and the mechanism was active. Without parentheses, the && bound to only the second sensor — so the first sensor alone could trigger the cutoff even when the mechanism was idle. The robot would randomly stop mid-match. The code was correct in the programmer's head; operator precedence made it wrong on the field. Adding four characters — two pairs of parentheses — fixed it.

🔌 System Check

⚙️ Where Operator Bugs Appear on Real Hardware

These aren't hypothetical — each one has caused a robot to behave unexpectedly at competition.

  • Integer division truncating a motor output to zero. If any motor output calculation involves dividing two int values, the result is always an integer. A command intended to set 50% speed evaluates to 0. Review every division that feeds into a motor.set() call and ensure at least one operand is a double.
  • The = vs == boolean assignment bug. Writing if (intakeRunning = true) makes the condition always true — the intake runs regardless of any other state. Java will warn on this, but warnings get ignored under time pressure. Prefer if (intakeRunning) to avoid the ambiguity entirely.
  • Unparenthesized mixed && / || conditions. Any safety gate or enable condition with more than two operands must have explicit parentheses. "I'm pretty sure the precedence is right" is not good enough for code that controls hardware capable of injuring someone.
  • Ramp rate missing from high-inertia mechanisms. Arms, elevators, and climbers with significant mass need software ramp rates even if the motor controller has one — because mechanical shock on enable can strip gears before the controller's configured ramp engages. If your mechanism ever jerks at enable, check what your output looks like in the first five loop cycles.
  • Modulo with negative values. Java's % operator can return negative remainders when the dividend is negative. Angle wrapping and mode cycling that only ever get positive inputs are fine. Any calculation that might go negative needs the ((x % n) + n) % n pattern to guarantee a positive result.

Knowledge Check

Click an answer to check your understanding.

What is the value of output after this executes, and what physical consequence would this have on a robot motor?

int speed = 1;  int divisor = 2;  double output = speed / divisor;
  • 10.5 — Java widens the result to double automatically
  • 20.0 — the division happens between two int values first, producing integer 0, which is then widened to double 0.0; the motor would not move
  • 31.0 — Java rounds integer division up before assigning to a double
  • 4A compile error — you cannot assign an int division result to a double
A student writes this safety check: if (tempSensor != null && tempSensor.getTemp() > 80.0). Why is && specifically the right operator here, rather than &?
  • 1& and && are identical for boolean expressions — the choice is purely stylistic
  • 2&& produces a boolean result; & would return an int and cause a compile error here
  • 3&& short-circuits — if tempSensor is null, the second condition is never evaluated and no NullPointerException is thrown; & would evaluate both sides regardless, crashing on a null sensor
  • 4& only works with integer types; it cannot be used with object references like tempSensor
A robot has four autonomous modes (0–3) and a button that should advance to the next mode, wrapping back to 0 after mode 3. Which expression correctly implements this?
  • 1autoMode = autoMode + 1;
  • 2autoMode++; if (autoMode > 3) autoMode = 3;
  • 3autoMode = (autoMode + 1) % 4;
  • 4autoMode = autoMode % 4 + 1;
💪 Practice Prompt

Build a Shooter Enable Condition

The following class skeleton represents a simplified shooter subsystem. Your job is to implement the operator logic — no WPILib hardware required, just pure Java logic. You can run this as a standalone Java class with a main() method.

public class ShooterLogic { // ── Constants ──────────────────────────────────────────────────── static final double SHOOTER_SPEED_RPM = 3500.0; static final double AT_SPEED_TOLERANCE = 150.0; // RPM static final double MAX_CURRENT_AMPS = 60.0; static final int NUM_AUTO_MODES = 4; // ── State (given — do not change) ──────────────────────────────── double currentSpeedRPM; // current shooter wheel speed double currentAmps; // stator current reading boolean shootButtonHeld; // driver input boolean intakeClear; // beam break clear = true boolean autoModeActive; // is robot in auto? int autoMode = 0; // which auto routine (0–3) // ── TODO: Implement these methods ──────────────────────────────── // Returns true if shooter is at target speed (within tolerance) boolean isAtSpeed() { // YOUR CODE HERE — use Math.abs() for the tolerance check } // Returns true if shooter should be enabled // Conditions: (button held AND at speed AND intake clear) OR auto mode active boolean shouldShoot() { // YOUR CODE HERE — use parentheses explicitly } // Returns true if a fault should be triggered // Conditions: current exceeds limit, but ONLY while shooter is commanded on boolean hasFault() { // YOUR CODE HERE } // Advances autoMode by 1, wrapping from 3 back to 0 void nextAutoMode() { // YOUR CODE HERE — one line with modulo } // Returns shooter output — ramp toward SHOOTER_SPEED_RPM in increments of 200 RPM/cycle // Never overshoot the target double getRampedSpeed() { // YOUR CODE HERE — use += and Math.min() } }
  1. Implement all five methods using only operators covered in this lesson. Use parentheses explicitly in every multi-operand condition.
  2. Add a main() method that tests all four combinations of shootButtonHeld and intakeClear for shouldShoot(), printing the result for each. Verify the truth table matches your expectation from the interactive table above.
  3. Test nextAutoMode() in a loop — call it 10 times starting from mode 0 and print the mode after each call. Confirm it cycles correctly.
  4. Bonus: The getRampedSpeed() method currently ramps up. Modify it to also ramp down — if the current speed is above the target, decrement by 200 RPM/cycle toward the target instead of stopping immediately. What operator change is needed?