Methods
You've been using methods since Lesson 1 — teleopPeriodic(), robotInit(), and motor.set() are all methods. This lesson is about writing your own: naming them well, giving them the right parameters, and using them to keep periodic code readable enough to debug at competition under pressure.
By the end of this lesson, you will:
- Write methods with correct syntax — access modifier, return type, name, parameters, and body
- Distinguish between
voidmethods that perform actions and methods that compute and return a value - Pass arguments to methods and explain why primitive arguments are passed by value
- Recognize method overloading and identify which overload Java selects at a given call site
- Refactor a bloated periodic method into focused helper methods, and explain why this matters for competition debugging
You've Already Written Methods
Every time you wrote public void teleopPeriodic() { ... }, you defined a method. WPILib calls it. Every time you wrote motor.set(0.6), you called a method that someone else defined. The syntax you've been writing since Unit 1 is method syntax — this lesson is about understanding it fully and writing your own from scratch.
In robot code, methods do two things: they organize your logic into named, reusable chunks, and they make teleopPeriodic() readable to someone who has never seen your robot. A 200-line periodic method that does everything is a debug nightmare at 7 AM before eliminations. A periodic method with six well-named helper calls is readable in ten seconds.
Anatomy of a Method
A Java method has up to six distinct parts. Click each highlighted section of the method below to learn what it does and why each choice matters in robot code.
void Methods vs. Methods That Return a Value
Every method either returns a value or it doesn't. This single design decision shapes how the method is used at the call site and what it's responsible for.
A void method performs an action — it changes state, sends a command to hardware, logs something, or updates a variable. It produces no value that the caller can use. The caller just runs it and moves on.
Most of the WPILib lifecycle methods you've written are void: they don't return anything to WPILib, they just do work. Motor commands, dashboard updates, and state changes are all void work.
Notice that calling a void method is a statement on its own — you can't assign it to a variable because there's nothing to assign. double x = updateDashboard() won't compile.
A method with a declared return type computes and sends back a value. The caller can use that value in an expression, assign it to a variable, or pass it directly to another method. The return keyword is mandatory — a non-void method that reaches the end without returning won't compile.
A common beginner mistake: writing a helper that should return a value but is declared void, so the caller has no way to use the result. If a method computes something a caller needs, it must return it.
Parameters and Arguments
Parameters are the named inputs declared in the method signature — they're local variables that exist only inside the method body. Arguments are the actual values passed in at the call site. The distinction matters for talking about code with teammates: "the method has two parameters" vs. "I called it with these two arguments."
This is the core value of parameters: the method is written once and called with different arguments. Without parameters, you'd write three nearly identical motor configuration blocks — three places to get out of sync, three places to update when a setting changes.
Pass-by-value for primitives
Java passes copies of primitive arguments into methods. Modifying the parameter inside the method has no effect on the original variable at the call site. This is one of the most common source of confusion for new Java programmers.
Primitive arguments (double, int, boolean) are passed by value — the method gets a copy. Object arguments (TalonFX, TalonFXConfiguration, arrays) are passed by reference — the method gets the same object the caller has. Modifying an object's fields inside a method does affect the original. This is why configureMotor(driveMotor, LIMIT) actually configures the real motor object — not a copy of it.
The Refactor: From Spaghetti to Structure
This is the most important skill in this lesson. A well-named method is documentation. A bloated periodic method is a maintenance liability. The tabs below show the same robot logic before and after a method extraction — compare the readability.
This is real code structure that appears in competition robots. Every new feature added to this file made the problem worse. Reading it cold at 6 AM before elimination rounds is its own challenge.
The behavior is identical. The periodic method now reads like a summary of what the robot does each cycle. Each helper method is short enough to understand on its own, and short enough to unit test independently.
The clamp() method at the bottom is called from both updateShooter() and updateArm(). One implementation, tested once, used everywhere. If you ever need to change the clamping logic — adding a deadband, logging when the value is out of range — you change it in one place.
I've reviewed robot code for hundreds of teams as an LRI and a mentor. The teams that can debug a problem in two minutes at competition aren't necessarily the ones with the most sophisticated control theory — they're the ones whose periodic methods read like English. When a mechanism stops working, the programmer looks at the four-line periodic, identifies the relevant helper, reads twenty lines of focused code, and finds the bug. On the teams with 200-line periodic methods, the programmer is still scrolling when the match starts.
Method Overloading
Java allows multiple methods to share the same name as long as their parameter lists differ — in the number, type, or order of parameters. Java uses the argument types at the call site to pick the right version at compile time. This is called overloading.
The return type alone is not enough to overload — two methods that differ only in return type won't compile. The difference must be in the parameters.
Standard configuration with a specified current limit. Used for any motor where you want explicit control over the limit.
configureMotor(driveMotor, 50);
Convenience overload using the class-wide default limit. Shorter call for the common case, delegates to the full version.
configureMotor(intakeMotor);
Full configuration with an additional inverted flag for motors that are physically mounted backwards.
configureMotor(rightMotor, 50, true);
The delegation pattern — shorter overloads calling the full-parameter version — means there's only one implementation of the actual configuration logic. When the electrical team changes the drive current limit, one number changes in one place and every configuration call that uses the default picks it up automatically.
Static Methods: Utilities That Don't Need an Object
A static method belongs to the class rather than to any specific instance of it. You call it using the class name, not an object reference. You've already used static methods throughout this course: Math.abs(), Math.min(), Math.max(), and SmartDashboard.putNumber() are all static.
If a method doesn't need to read or write any instance variables — it just transforms inputs to outputs using its parameters — it's a good candidate to be static. Math utilities, unit converters, and string formatters all fit this pattern. Making them static signals to readers that the method has no side effects on the object's state.
Before writing your own utility methods, check edu.wpi.first.math.MathUtil. It provides MathUtil.clamp(), MathUtil.applyDeadband(), MathUtil.angleModulus(), and more — all tested, documented, and used by thousands of teams. Your clamp() method from the refactor example is already there. In Unit 5, you'll use these directly when building your first drive code.
One Job Per Method
The most important design principle for robot code methods isn't a Java syntax rule — it's a discipline. A method should do one thing, named clearly enough that the method name is its own comment.
Ask yourself: can you describe what this method does without using the word "and"? If the answer is "it updates the shooter and logs to the dashboard and checks for faults," the method should be split. If the answer is "it calculates the P-term output for the arm," it's well-scoped.
During a pit debugging session I watched a student hunt for why their shooter was cutting out. The method in question was called handleShooter(). It did seven things. The fault-check logic — three conditions nested two levels deep — was in the middle of the motor-command logic. It took eleven minutes to isolate the relevant four lines. When they renamed and split the method, the bug became visible in under thirty seconds. The code was exactly the same; the structure was the difference. At competition, eleven minutes is usually longer than the time between your queue call and your match start.
🔌 System Check
- teleopPeriodic() should read like a table of contents. If your periodic method has more than ten non-trivial lines, extract helpers. The reader should understand what the robot does in each cycle by reading method names, not implementation details.
- Every helper that touches hardware should be named for the mechanism and the action.
updateArm(),commandShooter(),checkIntakeFaults()— notdoStuff(),helper1(), orperiodicLogic(). - Pure computation methods (no hardware side effects) should be
private static. A method that just does math is easiest to reason about and easiest to test when it's static. It signals clearly: this has no side effects. - Use overloading for convenience, delegation for correctness. Shorter overloads should call the full-parameter version — not duplicate logic. Duplicated logic in two overloads means two places to update when a hardware requirement changes.
- Check
MathUtilbefore writing a utility from scratch. Clamp, deadband, angle modulus, and interpolation are all in WPILib. Use the tested version. - Parameters that come directly from constants don't need to be parameters. A method that only ever receives
SCORE_ANGLE_DEGfor a particular argument doesn't need that parameter — just reference the constant directly. Parameters are for values that genuinely vary at the call site.
Knowledge Check
Click an answer to check your understanding.
private double calculateError() reaches the end of its body without a return statement. What happens?double speed parameter and modifies it inside the body with speed *= 0.5. After the method returns, the caller's double requestedSpeed variable that was passed as the argument — what is its value?void configure(TalonFX motor, int limit) and void configure(TalonFX motor). A third programmer tries to add double configure(TalonFX motor, int limit). What does Java do?Extract, Name, and Overload
The following teleopPeriodic() method is functional but structured in a way that will cause debugging problems at competition. Refactor it completely.
- Extract the climber logic, intake logic, and dashboard updates into three separate
private voidhelper methods with names that clearly describe what each does. Replace the body ofteleopPeriodic()with three calls. - Replace every magic number (
0.6,-0.4,0.75,60.0) with namedstatic finalconstants declared at the top of the class. Add a comment to each constant explaining what physical constraint it represents. - Extract the stall-current guard (the
if > 60.0block) into aprivate boolean isStalled(TalonFX motor, double limitAmps)method that can be reused for both the climber and the intake. Show how you call it in both helpers. - Write an overloaded version
private boolean isStalled(TalonFX motor)that uses a class constantDEFAULT_STALL_AMPSas the limit, and delegates to the full version. Add a call to this overload in one of your helpers. - Bonus: Write a
private static double clamp(double value, double min, double max)utility and use it in both the climber and intake helpers to cap outputs. Then look upMathUtil.clamp()in the WPILib documentation and compare the signatures. What is different, and which would you use on a competition robot?