No phone? Tell a mentor and we'll sign you in.
One blueprint, as many robot parts as you need
Last session's Intake.java is back. You can read most of it now.
But some words we said to "just copy for now." They're circled in gold.
Today you learn what every one means.
SwerveModule
One drawing. Says what every module has and does.
Same design. Each object has its own motor ID and its own current angle.
driveIdanglespeeddrive()setAngle()stop()motorbeamBreakrun()stop()hasGamePiece()msg.length()Ask the String object msg how many characters it hasSystem.out.println("Hi")Ask the screen-printing object System.out to printmotor.setThrottle(0.5)Ask the motor object to spin at half power
public class Battery {
String name;
int year;
double voltage;
boolean isMatchReady() {
return voltage >= 12.0;
}
}
new builds an object from the classBattery a = new Battery(); a.name = "2025-A"; a.year = 2025; a.voltage = 12.8; System.out.println(a.isMatchReady());
trueBatteryname · year · voltage
isMatchReady()
Click a battery's voltage to change it.
Press new Battery() to build one.
a.isMatchReady()trueb.isMatchReady()falseEach call uses that object's own voltage.
BatterynewspareBatteryspareBattery is one battery.public class Battery { ... }
The blueprint
public class Main { main ... }
Uses it: new Battery()
Main.java can use Battery without any import, because both files are in the same folder.
That folder is called a package. More in section 7.
C:\bootcamp, then open the C:\bootcamp\session3 folder in WPILib VS Code. Each exercise is its own folder inside it: ex01, ex02... The first line of each file says package ex01;. Leave it alone for now.describe() that prints the name, year and voltage.new and fill in their fields.describe() and isMatchReady() on each one.int age(int thisYear).2025-A (2025): 12.8 V match-ready: true 2024-B (2024): 11.8 V match-ready: false 2023-C (2023): 12.6 V match-ready: true
name, year and voltage.Battery c = new Battery(); System.out.println(c.name); System.out.println(c.isMatchReady());
null
falseFields you don't set get default values: 0, 0.0, false, or null for objects.
What if Java made you fill in the important parts when you build it?
public class Battery {
String name;
int year;
double voltage;
public Battery(String name, int year, double voltage) {
this.name = name;
this.year = year;
this.voltage = voltage;
}
// ...isMatchReady() as before
}
Battery a = new Battery("2025-A", 2025, 12.8);this means "my own"new Battery("2025-A", 2025, 12.8)Temporary. Gone when the constructor ends.
this object's fieldsKept as long as the object lives.
voltage = voltage; copies the parameter into itself. The field is never set and stays 0.0. Always write this.voltage = voltage;new Battery() stops workingMain.java:5: error: constructor Battery in class Battery
cannot be applied to given types;
Battery spare = new Battery();
^
required: String,int,double
found: no argumentsnew Battery("Spare", 2024, 12.6)
public class Intake {
private final PWMSparkMax motor;
private final DigitalInput beamBreak;
public Intake(int motorChannel, int sensorChannel) {
motor = new PWMSparkMax(motorChannel);
beamBreak = new DigitalInput(sensorChannel);
}
}
Intake front = new Intake(0, 1); Intake back = new Intake(2, 3);
The channel numbers move into the constructor. One class, two separate intakes.
new SwerveModule(1, 2); new SwerveModule(3, 4); new SwerveModule(5, 6); new SwerveModule(7, 8);
Write the class once. Build it four times with different IDs.
2025-A: 0.0 V ready: false 2024-B: 0.0 V ready: false 2023-C: 0.0 V ready: false
Back in 10 minutes. Next: arrows, and the most famous crash in Java.
A primitive (int, double, boolean, char...) sits right inside its box.
A String box holds an arrow.
So does every object variable. The arrow is called a reference.
== compares arrows. .equals() compares contents.mode == targetfalseSame letters, but two different objects in two places. The arrows are different.
mode.equals(target)true.equals() looks inside and compares the characters.
.equals().Battery spare = null;
The variable exists. The battery doesn't.
Object fields you never set start as null too (remember c.name printing null?).
Primitives can't be null. An int always holds a number.
public class Intake extends SubsystemBase {
private PWMSparkMax motor; ⟦// no new!⟧
public void stop() {
motor.setThrottle(0.0);
}
}
Unhandled exception: java.lang.NullPointerException:
Cannot invoke "PWMSparkMax.setThrottle(double)"
because "this.motor" is null
at frc.robot.subsystems.Intake.stop(Intake.java:5)new, where it's declared or in the constructor.Exception in thread "main"
java.lang.NullPointerException:
Cannot invoke "ex03.Motor.setThrottle(double)"
because "this.rightMotor" is null
at ex03.Drivetrain.drive(Drivetrain.java:15)
at ex03.Main.main(Main.java:10)setup(). Is it filling in the field, or making a brand-new variable with the same name?
public class Battery {
!! static int batteriesMade = 0;
double voltage;
public Battery(double voltage) {
this.voltage = voltage;
batteriesMade++;
}
}
batteriesMade0main static?
!!public static void main(String[] args) {
When the program starts, no objects exist yet.
So main has to work without an object. It belongs to the class: static.
Math.max(3, 7) → 7
Math.abs(-0.4) → 0.4
No new Math() needed. You call them on the class name, because they don't need any object's data.
public static void main(String[] args) {
⟦describe()⟧;
}
error: non-static method describe() cannot be
referenced from a static context
public static void main(String[] args) {
Battery a = new Battery("2025-A", 2025, 12.8);
a.describe();
}
public class SwerveModule {
double angle;
}
static finalprivate static final int kMotorChannel = 0; private static final int kSensorChannel = 1; private static final double kIntakeSpeed = 0.75;
static to SwerveModule.java.FL angle: 45.0 FR angle: 90.0 FL is driving at 0.5 CHECK PASSED: each module has its own angle
SwerveModule fl = new SwerveModule("FL"); then fl.setAngle(45);Back in 10 minutes. Next: private, packages, and lots of objects.
// "I'll just make it faster" motor.speed = ⟦5.0⟧;
A motor only understands -1.0 to 1.0. Nothing stops 5.0 from getting in.
And a teammate's code could change it in the middle of a match.
private: only this class can touch itReady.
public class SafeMotor {
private double speed;
public void setSpeed(double speed) {
this.speed = Math.clamp(speed, -1.0, 1.0);
}
public double getSpeed() {
return speed;
}
}
speed private.setSpeed clamp the value to -1.0 to 1.0.setSpeed(0.5) -> 0.5 PASS setSpeed(3.0) -> 1.0 PASS setSpeed(-7.0) -> -1.0 PASS setSpeed(-0.25) -> -0.25 PASS
Math.clamp(speed, -1.0, 1.0). An if / else if works too.package frc.robot.subsystems;
package frc.robot.subsystems; import org.wpilib.drivers.motor.PWMSparkMax;
"I'm using the PWMSparkMax class from WPILib's org.wpilib.drivers.motor package."
error: cannot find symbol
symbol: class PWMSparkMax
location: class IntakeVS Code's light bulb (Ctrl + .) adds the import for you.
Every file is one class, in a folder that matches its package.
@@1SwerveModule[] modules = new SwerveModule[4]; @@2modules[0] = new SwerveModule(1, 2); @@3modules[1] = new SwerveModule(3, 4); @@3modules[2] = new SwerveModule(5, 6); @@3modules[3] = new SwerveModule(7, 8);
Four slots, numbered 0 to 3. Each one starts as null.
Last slot is length - 1. modules[4] crashes: ArrayIndexOutOfBoundsException.
(1, 2)(3, 4)(5, 6)(7, 8)
for (SwerveModule m : modules) {
m.stop();
}
Read it as: "for each module m in modules, stop it."
import java.util.ArrayList;
ArrayList<Battery> cart = new ArrayList<>();
cart.add(new Battery("2025-A", 2025, 12.8));
cart.add(new Battery("2024-B", 2024, 12.3));
System.out.println(cart.size()); // 2
System.out.println(cart.get(0).name); // 2025-A
cart.get(cart.size()) crashes. The last one is size() - 1.ArrayList<Battery> called cart.2025-A 12.8 V ready 2025-B 11.8 V charge me 2024-A 12.7 V ready 2024-B 11.9 V charge me 2023-A 12.5 V ready 3 of 5 batteries are match-ready
int ready = 0; before the loop and add 1 inside an if.periodic()setDefaultCommand(...)... and morerun()stop()hasGamePiece()+ everything above, for free
public class Intake extends SubsystemBase {
@Override
public void periodic() {
// runs every 20 ms
}
}
@Override: "I'm replacing a method I inherited." The robot calls periodic() every 20 ms (the loop from our first session).
The details come in a later session on command-based code. For now: an Intake is a Subsystem.
Class written in Intake.java → object built in RobotContainer → methods called every loop.
Click a layer to turn it on or off.
loop 0: motor 0.75 piece: false loop 19: motor 0.75 piece: false loop 20: motor 0.0 piece: true PASS motor runs before the piece PASS motor stops when the piece arrives PASS hasGamePiece() is true at the end 3 / 3 checks passed
hasGamePiece() returns !beamBreak.get(). Same as Intake.java.A class is a blueprint. new builds an object.
Constructors set up new objects. Use this.
Object variables hold arrows. null means no object.
static belongs to the class. Never use it to silence errors.
Fields private, useful methods public.
new do?b = a; b.voltage = 11.9; Now a.voltage is...main static?voltage = voltage; in a constructor?
VS Code, WPILib, Git and GitHub, build and deploy. Finish the exercise pack before then.
| →SpacePgDnEnter | Next step or slide (also: click the right half of the screen, or a clicker) |
| ←PgUpBackspace | Back (also: click the left half, or right-click) |
| Home End | First slide / last slide |
| 12… then Enter | Jump to a slide number |
| T | Contents: jump to any slide (or the ☰ button, top-left) |
| N | Show or hide speaker notes |
| F | Fullscreen on / off |
| ? | This help |
| Esc | Close notes, contents, help or a photo |