OOP - Classes & Objects
the most important week in this course fr. everything in FRC robot code is a class. don't skip this!!
Why this matters: every subsystem (a distinct part of the robot — intake, drivetrain, shooter — represented as a Java class) on 2974's robot is a class. Drivetrain.java, Shooter.java, Coral.java, Finger.java — all classes. understanding this week means you can read and write real robot code. OOP is confusing at first ngl but once it clicks it clicks fr.
Classes vs Objects
here's the single most important concept in this entire course: a class is a blueprint. an object is one specific thing you built from that blueprint. you write the class once. you can create as many objects from it as you want, and each one is completely independent from the others.
class vs object — the blueprint and the thing
ok let's start with the cookie cutter analogy because it's genuinely the best one. a cookie cutter is just a piece of metal bent into a shape — a star, a gingerbread man, whatever. the cutter itself doesn't taste like anything. it doesn't have sprinkles. it just defines the shape. every cookie you actually cut out IS a cookie — it exists in the real world and it's its own separate thing. you can frost one cookie and leave the others plain. you can eat one and the rest are fine. changing one cookie does nothing to any other cookie.
the cookie cutter = the class. each individual cookie = an object (also called an instance). the class defines the template — what shape the thing has, what data it holds, what it can do. each object you create from that class is its own independent living thing with its own copy of all the data.
second analogy just to make it stick: imagine a blueprint for a house. the blueprint lives in an architect's office. it says "2 bedrooms, 1 bathroom, open kitchen, attached garage." the blueprint itself isn't a house — you can't sleep in it. but from that ONE blueprint, a construction company can build 50 identical houses. each house is separate. what happens in one house doesn't affect any other house. if the family in house #7 paints their walls green, house #12 stays white. blueprint = class. each built house = an object.
the FRC connection — why this is the most important week
on team 2974, every single mechanism on the robot is modeled as a class. DriveSubsystem.java is a class — it's the blueprint. when the robot boots up, it builds ONE object from that blueprint: new DriveSubsystem(). that one object represents your actual robot's drivetrain. same exact deal for Shooter.java, Intake.java, Climber.java — every subsystem file you'll ever write is a class, and each file gets instantiated into exactly one object that lives for the entire match.
this is the moment in this course where things go from "learning Java syntax" to "actually understanding how robot code is structured." classes and objects aren't just an academic concept — they ARE the architecture of every WPILib (WPILib is the Java library all FRC teams use for robot programming — it gives you SubsystemBase, Commands, Triggers, and tons of utilities)-based robot program ever written.
WRT connection: look at the actual Rebuilt repo. every file in the subsystems/ folder is a class. Robot.java creates ONE instance of each subsystem using new. those instances get passed around to commands so everything has access to the actual hardware objects. the class is just the design — new is what builds the real thing.
anatomy of a class — every part labeled
let's look at a complete class with every single element called out. don't just skim past the comments in this one — every line has a specific reason for being there.
// "public class Motor" — this is the CLASS DECLARATION // "public" = anyone anywhere in the project can use this class // "class" = this keyword tells Java this is a class definition (not an interface, not an enum) // "Motor" = the name of the class (always PascalCase — capitalize first letter of every word) public class Motor { // FIELDS (instance variables) — the data each Motor object stores // declared inside the class but outside any method // every Motor object you create gets its OWN copy of these three values // "private" = only code written inside Motor.java can access these directly private int m_id; // CAN ID — the unique number identifying this motor on the robot's wiring system private double m_speed; // current speed (-1.0 to 1.0) private boolean m_isInverted; // whether positive speed means backward // CONSTRUCTOR — runs automatically when you write "new Motor(...)" // three rules: same name as the class (Motor), no return type (NOT void), set initial state public Motor(int id, boolean isInverted) { m_id = id; // save the port number m_isInverted = isInverted; // save whether it needs to be flipped m_speed = 0.0; // always starts at rest — never spinning on creation } // METHODS — what this Motor can do, and what info it can give you // instance methods belong to a specific object and can access its fields public void setSpeed(double speed) { // apply inversion: if this motor is inverted, negate the speed // this is why the left and right drivetrain motors spin correctly // even though they're physically facing opposite directions m_speed = m_isInverted ? -speed : speed; } public double getSpeed() { return m_speed; // give back the current speed so other code can read it } }
creating objects and proving independence
once you have a class, you create objects with the new keyword. here's the critical thing to prove to yourself: once you have two objects, they are completely independent. changing one does NOT change the other. at all. ever.
// "new Motor(1, false)" — creates a brand new Motor object in memory // Java allocates space for it, runs the constructor, returns a reference to it // leftMotor and rightMotor are TWO COMPLETELY SEPARATE objects // they happen to be the same "type" (Motor) but they are different things Motor leftMotor = new Motor(1, false); // port 1, not inverted Motor rightMotor = new Motor(2, true); // port 2, INVERTED (right side of drive) // dot notation: call methods ON a specific object // leftMotor.setSpeed(0.5) means "run setSpeed on the leftMotor object specifically" leftMotor.setSpeed(0.5); rightMotor.setSpeed(0.5); // rightMotor is INVERTED, so 0.5 becomes -0.5 — they spin in opposite directions // this is exactly how a real tank drive (tank drive = the simple drivetrain style where left stick controls left wheels, right stick controls right wheels) works on an FRC robot!! System.out.println(leftMotor.getSpeed()); // 0.5 System.out.println(rightMotor.getSpeed()); // -0.5 // NOW: prove independence by stopping leftMotor only leftMotor.setSpeed(0.0); // leftMotor is stopped. rightMotor is COMPLETELY UNAFFECTED. // they are separate objects with separate copies of m_speed System.out.println(leftMotor.getSpeed()); // 0.0 — stopped System.out.println(rightMotor.getSpeed()); // -0.5 — still going!!
what does new actually do?
when you write new Motor(1, false), three things happen in exact order and understanding all three will save you from so much confusion later:
- allocate memory — Java finds a chunk of RAM big enough to hold all of Motor's fields (m_id, m_speed, m_isInverted) and reserves it for this object
- run the constructor — your code inside
Motor(int id, boolean isInverted)executes, setting the initial values for that chunk of memory - return a reference — Java gives you back an address (a reference) pointing to where in memory that object now lives. the variable
leftMotorholds this address, not the object itself
reference types: a variable like Motor m doesn't HOLD a Motor object inside it — it holds an ADDRESS pointing to where in memory the Motor lives. that's why Motor, String, and all class types are called "reference types." primitives (int, double, boolean) store their actual value. objects store an address. this distinction will matter a lot when you hit null pointer exceptions ngl.
the forgetting-new gotcha (this WILL bite you)
here's the most common mistake when you're first learning objects. you declare a variable of a class type, you plan to use it, but you forget the new keyword. the variable gets created... but it holds null — which means "no object, nothing, empty." then the moment you try to call a method on it, Java blows up with a NullPointerException. it's not a compile error so you won't catch it until the code actually runs.
// BAD — declared the variable but never created the object // leftMotor is null right now — it doesn't point to anything Motor leftMotor; // or even worse — explicitly assigned null: Motor leftMotor = null; // now you try to use it... leftMotor.setSpeed(0.5); // CRASH: NullPointerException at runtime!! // "you can't call a method on nothing" // ───────────────────────────────────────── // GOOD — actually create the object with new Motor leftMotor = new Motor(1, false); // now it points to a real Motor object leftMotor.setSpeed(0.5); // works perfectly
Motor.java is a class.new. Each object has its OWN independent copy of every field. Also called an "instance."new to create any object.leftMotor.setSpeed(0.5) calls setSpeed specifically on leftMotor — not rightMotor.Topic 1 — Coding Prompt
Write a Motor class with:
• private fields: int m_id, boolean m_inverted, double m_speed
• constructor Motor(int id, boolean inverted) — sets id and inverted, starts speed at 0
• void setSpeed(double speed) and double getSpeed()
Then in main:
• create a left motor (id=1, not inverted) and a right motor (id=2, inverted)
• set both speeds to 0.6, then print both
• set ONLY the left motor to 0.0, then print both again
After stopping left, right should still show 0.6. If both change, something's wrong.
Topic 1 — Quick Check
Constructors & Fields
fields are the state of an object — the things it knows about itself. the constructor is the code that runs when the object first comes to life. together they define what an object IS and what it starts as.
what even ARE fields?
think of fields like a hospital patient wristband. the moment you check into a hospital, a nurse puts a bracelet on your wrist with your name, date of birth, blood type, and patient ID. that's YOUR data — not some generic person's data. every patient has their own bracelet with their own values. your wristband says your name, the next patient's wristband says their name. changing one bracelet doesn't touch any other bracelet in the whole hospital.
fields work exactly the same way. when you create a Motor object, Java copies the field template from the class and gives THAT SPECIFIC OBJECT its own little chunk of memory to hold its own values for m_id, m_speed, and m_isInverted. leftMotor's m_speed is physically stored in a different place in memory than rightMotor's m_speed. they just happen to have the same names because they're the same type.
fields are declared at class level — inside the class body, but outside any method. they exist for the entire life of the object. every time a method runs, it can see and modify these fields.
public class Motor { // FIELDS — declared at class level, outside any method // "private" = only Motor's own methods can touch these // "m_" prefix = WRT convention: m stands for "member variable" // this makes fields visually distinct from local variables at a glance // when you see m_speed in a method, you immediately know: that's a field private int m_motorID; // CAN ID — the unique number identifying this motor on the robot's wiring system private double m_speed; // current commanded speed (-1.0 to 1.0) private boolean m_isRunning; // whether the motor is actively spinning // ... constructor and methods go below }
constructors — the object's birth certificate
a constructor runs automatically, exactly once, the moment you write new ClassName(...). it's your one guaranteed chance to set up the object in a valid state before anything else can touch it. think of it as the birth certificate moment — the instant the object is born, it gets its starting values stamped in.
two absolute rules you need to burn into memory:
- the constructor name must be exactly the same as the class name — capital letters and all
- constructors have no return type — not
void, notint, notdouble. nothing. just the name and parentheses. if you addvoid, Java treats it as a regular method, not a constructor, and it will never run onnew
public class Motor { private int m_motorID; private double m_speed; private boolean m_isRunning; // VALID CONSTRUCTOR — same name as class, no return type // Java calls this automatically when you write new Motor(5) public Motor(int motorID) { m_motorID = motorID; // set initial state here m_speed = 0.0; // always starts stopped m_isRunning = false; // always starts not running } // INVALID — has "void" before the name // Java does NOT treat this as a constructor!! it's just a regular method // calling new Motor(5) would NOT call this — your fields would stay at defaults // public void Motor(int motorID) { ... } <-- do NOT do this // INVALID — wrong capitalization (Java is case-sensitive) // "motor" != "Motor" — this is also just a regular method // public motor(int motorID) { ... } <-- also wrong }
the this keyword — "the current object"
inside any instance method or constructor, this is a reference to the object the code is currently running on. if you have Motor leftMotor and Motor rightMotor and you call leftMotor.setSpeed(0.5), then inside setSpeed, this refers to leftMotor. call it on rightMotor instead, and this becomes rightMotor. this is always "the object right now."
the most common place you'll write this is in constructors when a parameter has the same name as a field. without this, Java can't tell which one you mean:
public class Motor { private int m_motorID; // what if the parameter name exactly matches the field name? // Java has to pick ONE — it picks the local one (the parameter) // so "motorID = motorID" assigns the parameter to itself, doing nothing // m_motorID stays 0. the bug is SILENT — no error, wrong behavior forever public Motor(int motorID) { motorID = motorID; // BUG: assigns parameter to itself, field never set!! } }
public class Motor { private int m_motorID; private double m_speed; private boolean m_isInverted; public Motor(int motorID, boolean isInverted) { // "this.m_motorID" = the field on THIS specific object (what we want to set) // "motorID" = the parameter that was passed in (the new value) // left side is the destination, right side is the source — just like any assignment this.m_motorID = motorID; // field = parameter: correct!! this.m_isInverted = isInverted; this.m_speed = 0.0; // no conflict here but using this for clarity } }
WRT tip: using the m_ prefix on fields mostly avoids the naming conflict problem entirely — your field is m_motorID and your parameter is motorID, so they never clash. but you will see this in tons of Java code in the wild (especially without the m_ convention), so make sure you know exactly what it means when you encounter it.
the motorID = motorID gotcha (this WILL bite you)
let me tell you about a bug that has ruined many first Java projects. you write a class, you write a constructor, you test it — everything seems to work. but your object's fields are always 0, always false, always empty. you're setting them in the constructor, you swear you are. then you stare at this for 30 minutes and finally see it: motorID = motorID. assigning a parameter to itself. the field never got set. never a warning. never an error. just silent wrong behavior.
// BUGGY VERSION — field is never actually set public class Motor { private int motorID; // no m_ prefix (this sets up the name clash) public Motor(int motorID) { motorID = motorID; // assigns the parameter to itself. field stays 0. } public int getID() { return motorID; } } Motor m = new Motor(5); System.out.println(m.getID()); // prints: 0 ... NOT 5. silent bug!! // ───────────────────────────────────────── // FIXED VERSION — use m_ prefix to prevent the name clash public class Motor { private int m_motorID; // m_ prefix = field, different name from parameter public Motor(int motorID) { m_motorID = motorID; // unambiguous: left is field, right is parameter } public int getID() { return m_motorID; } } Motor m = new Motor(5); System.out.println(m.getID()); // prints: 5 — correct!!
common gotcha: Java does NOT warn you about motorID = motorID unless your IDE has a specific lint rule for it. it's valid Java. it just does nothing useful. the fix: always use m_ on instance variables on WRT. that way the field and the parameter can never have the same name.
m_ prefix on WRT. Usually private.new. Sets initial state of the object.this inside a method is the object the method was called on. Used to clarify field vs parameter when names clash.m_: m_speed, m_isRunning, m_motorID. Visually distinct from local vars instantly.Topic 2 — Coding Prompt
Write a class called LimitSwitch with:
• private fields: int m_port, boolean m_isPressed, String m_name
• constructor LimitSwitch(int port, String name) — sets port and name, starts isPressed as false
• void press() — sets m_isPressed to true
• void release() — sets m_isPressed to false
• boolean isPressed() — returns the current state
In main, create two switches. Press switch 1. Check that switch 2 is still not pressed — they should be independent.
Topic 2 — Quick Check
Access Modifiers
access modifiers are keywords that control who can see and use a field or method. they're how you protect your object's data from being incorrectly modified by other parts of the code. this is the "control" part of being in control of your own data.
what even ARE access modifiers?
imagine a filing cabinet with three different drawers. the bottom drawer is locked with a key only you have — nobody else can open it. the middle drawer is unlocked but in your private office — only your family (people in the same building) can get to it. the top drawer sits in the lobby and literally anyone who walks in can open it.
that's the three access modifiers you'll use. private is the bottom drawer — locked, only this class can open it. protected is the middle drawer — this class and its subclasses (more on that in week 6). public is the top drawer in the lobby — anyone anywhere in the project can touch it directly.
| Modifier | Who can access it | When to use it |
|---|---|---|
public | Anyone, anywhere in the project | Methods you want other classes to call (getters, setters, actions) |
private | Only inside the class it's declared in | Instance variables — almost always. Use this as your default. |
protected | This class and its subclasses | When designing for inheritance (week 6 territory) |
why private fields? the DangerousMotor story
imagine you're writing a motor wrapper class and you make m_speed public. "it's fine," you think, "I'll just be careful." three weeks later, someone on the team is writing drive code at 11pm during build season and they type driveMotor.m_speed = 9999.0 instead of driveMotor.setSpeed(0.99). compiles fine. deploys fine. robot arm goes full power to 9999 in a direction it shouldn't and now you're explaining to your parents why you need to buy a new motor. this is not a hypothetical. this is why we use private.
// BAD — public field, anyone can bypass your logic and write garbage values public class DangerousMotor { public double speed; // no protection at all!! } // anywhere in the project, someone can do this: DangerousMotor m = new DangerousMotor(); m.speed = 9999.0; // compiles fine. no error. your motor is cooked. m.speed = -500.0; // also fine. also bad. m.speed = Double.NaN; // Java lets you. hardware does not appreciate this. // ───────────────────────────────────────────────────────────── // GOOD — private field with a setter that validates input public class SafeMotor { private double m_speed; // can only be changed through setSpeed() public void setSpeed(double speed) { // Math.max(-1.0, ...) ensures speed is never below -1.0 // Math.min(1.0, ...) ensures speed is never above 1.0 // this is called "clamping" — it's in every motor wrapper ever written m_speed = Math.max(-1.0, Math.min(1.0, speed)); } public double getSpeed() { return m_speed; } } SafeMotor m = new SafeMotor(); m.setSpeed(9999.0); // setSpeed clamps it to 1.0. motor is fine. m.setSpeed(-500.0); // clamped to -1.0. safe. // m.m_speed = 9999.0; // COMPILE ERROR: m_speed has private access in SafeMotor
encapsulation — the big picture
the pattern of private fields plus public methods is called encapsulation. it's one of the core principles of object-oriented programming. the idea: your object owns its data. other code can interact with it, but only through the doors you provide (the public methods). you control what goes in and what comes out. nobody can reach in and corrupt your state directly.
on WRT, the rule is absolute: all instance variables are private. no exceptions. if another class needs to read the value, you write a getter. if it needs to change the value, you write a setter with validation. this isn't bureaucracy — it's what makes real robot code survive a full season without random crashes caused by someone setting a field to an invalid value on a tired Tuesday night during build season.
WRT rule: ALL instance variables are private. period. always. no exceptions. if another class needs the value, write a public getter. if it needs to change the value, write a public setter with validation. this is non-negotiable on 2974.
public methods — the intended interface
while fields should be private, most of your methods should be public. methods are HOW other code interacts with your object — they're the public interface. you want to make it easy for commands and other subsystems to call your methods, so keep the methods public. keep the data private.
public class Intake { // fields: PRIVATE — other classes cannot directly touch these private double m_speed; private boolean m_isRunning; public Intake() { m_speed = 0.0; m_isRunning = false; } // methods: PUBLIC — these are the "doors" other classes use to interact public void run(double speed) { m_speed = Math.max(0.0, Math.min(1.0, speed)); // intake only goes forward m_isRunning = true; } public void stop() { m_speed = 0.0; m_isRunning = false; } public boolean isRunning() { return m_isRunning; } public double getSpeed() { return m_speed; } }
Topic 3 — Coding Prompt
Write a Shooter class with a private double m_flywheelSpeed field.
Write a setter setFlywheelSpeed(double speed) that protects the field — the stored value should never go below 0.0 or above 1.0. If someone passes 2.5, store 1.0. If they pass -0.5, store 0.0.
Also write:
• getFlywheelSpeed() — returns the stored speed
• boolean isReadyToShoot() — returns true if speed is above 0.8
Test: set speed to 2.5, then call getFlywheelSpeed(). It should return 1.0, not 2.5.
Topic 3 — Quick Check
Methods & Encapsulation
you learned how to write methods in week 4. now they're inside a class, which makes them instance methods — methods that belong to a specific object and can directly access that object's private fields. this is where everything comes together.
instance methods — behavior that belongs to an object
in week 4, some methods had the static keyword — Math.abs(-5), Math.max(a, b). you called those on the CLASS directly, no object needed. static methods are just utility functions that belong to the class as a whole, not to any particular object. they can't access fields because there's no "this object" when you call them.
instance methods are completely different. they belong to a specific object. when you call leftMotor.setSpeed(0.5), the setSpeed method runs in the context of leftMotor. it can see m_speed, m_id, m_isInverted — all of leftMotor's private fields directly. that's the whole point. the method knows which object it belongs to, so it can read and modify that object's state.
public class Motor { private double m_speed; // INSTANCE METHOD — belongs to each individual Motor object // can access m_speed, m_id, m_isInverted — anything on "this" // called as: someMotor.setSpeed(0.5) public void setSpeed(double speed) { m_speed = speed; // this.m_speed = speed (this is implied) } // STATIC METHOD — belongs to the Motor CLASS, not any object // no access to m_speed or "this" — there's no object context here // called as: Motor.clamp(1.5) — no object needed // useful for pure utility functions that don't need object state public static double clamp(double value) { return Math.max(-1.0, Math.min(1.0, value)); // pure math, no fields needed } } // calling them: Motor m = new Motor(1, false); m.setSpeed(0.5); // instance method — need an object double safe = Motor.clamp(5.0); // static method — called on the class directly
the getter/setter pattern — using it correctly
the full getter/setter pattern shows up in literally every subsystem you will ever write on this team. private field. public getter that returns it. public setter that validates before storing. here's the naming convention: getters are getX(), but for booleans, use isX() instead (reads more naturally: if (motor.isRunning()) vs if (motor.getIsRunning())). setters are always setX(value).
public class Motor { private int m_motorID; private double m_speed; private boolean m_isRunning; public Motor(int motorID) { m_motorID = motorID; m_speed = 0.0; m_isRunning = false; } // GETTERS — return the private value, no parameters, just return // naming: getX() for most types, isX() for booleans (reads naturally) public double getSpeed() { return m_speed; } public int getMotorID() { return m_motorID; } public boolean isRunning() { return m_isRunning; } // isX() for booleans!! // SETTERS — validate input, then store it // naming: setX(value) where X matches the field name public void setSpeed(double speed) { // clamp to [-1.0, 1.0] so nothing can set an unsafe value m_speed = Math.max(-1.0, Math.min(1.0, speed)); } // ACTION METHODS — verbs that do something meaningful using the object's state public void start(double speed) { setSpeed(speed); // call our own setter — reuse its validation!! m_isRunning = true; } public void stop() { m_speed = 0.0; m_isRunning = false; } /** returns a human-readable status line for logging */ public String getStatusString() { return String.format("Motor %d: %.2f | %s", m_motorID, m_speed, m_isRunning ? "RUNNING" : "STOPPED"); } }
a real-looking FRC subsystem
ok here's the payoff. everything from this whole week — classes, fields, constructors, access modifiers, getters, setters, action methods — assembled into something that looks like actual robot code. this is basically what you'd write in a real match season if someone said "write the shooter subsystem."
/** * Models the shooter mechanism. * Manages flywheel speed and tracks whether we're at target velocity. * On WRT, every subsystem class looks like this. */ public class ShooterSubsystem { // all fields: private, m_ prefixed — state owned by THIS object private int m_topMotorID; private int m_bottomMotorID; private double m_targetSpeed; // what speed we're ASKING for (0.0 to 1.0) private boolean m_isAtSpeed; // whether flywheel has actually reached that speed // constructor: takes motor CAN IDs, sets safe starting state public ShooterSubsystem(int topID, int bottomID) { m_topMotorID = topID; m_bottomMotorID = bottomID; m_targetSpeed = 0.0; // starts off — never spin on boot without being asked m_isAtSpeed = false; // definitely not at speed when we just turned on } /** Sets the desired flywheel speed, clamped to [0.0, 1.0]. Shooters don't go backward. */ public void setTargetSpeed(double speed) { m_targetSpeed = Math.max(0.0, Math.min(1.0, speed)); } /** Spins up the shooter to full power. Short for setTargetSpeed(1.0). */ public void spinUp() { setTargetSpeed(1.0); // reuse the setter so we still get validation } /** Stops the flywheel and clears at-speed flag. */ public void stop() { m_targetSpeed = 0.0; m_isAtSpeed = false; // if we're stopping, we're definitely not at speed anymore } /** @return current target speed (0.0 to 1.0) */ public double getTargetSpeed() { return m_targetSpeed; } /** @return true if the flywheel has reached target velocity */ public boolean isAtSpeed() { return m_isAtSpeed; } /** @return formatted status string for Shuffleboard */ public String getStatusString() { return String.format("Shooter: %.2f | %s", m_targetSpeed, m_isAtSpeed ? "AT SPEED" : "SPINNING UP"); } } // usage in Robot.java — this is basically how it works in the real codebase ShooterSubsystem m_shooter = new ShooterSubsystem(7, 8); // motors on CAN 7 and 8 m_shooter.spinUp(); System.out.println(m_shooter.getTargetSpeed()); // 1.0 System.out.println(m_shooter.isAtSpeed()); // false (in reality: updated by sensors) System.out.println(m_shooter.getStatusString()); // "Shooter: 1.00 | SPINNING UP"
WRT method conventions: getters are getX() (or isX() for booleans), setters are setX(value), and action methods are descriptive verbs: spinUp(), stop(), deploy(), retract(). keep the names obvious. someone reading your code at 2am during build season should not have to think about what a method does.
calling your own methods from inside the class
one thing that's easy to miss: methods inside a class can call OTHER methods inside the same class. you saw it above — spinUp() calls setTargetSpeed(1.0). this is huge because it means you write the validation logic ONCE in the setter, and every other method that needs to set speed just calls the setter. you don't copy-paste the clamping math five places. one place, one set of logic, everything stays consistent.
public class Intake { private double m_speed; private boolean m_isRunning; public Intake() { m_speed = 0.0; m_isRunning = false; } // setter: all validation logic lives HERE and only here public void setSpeed(double speed) { m_speed = Math.max(0.0, Math.min(1.0, speed)); } // action methods just call setSpeed() — they don't duplicate the clamping public void runSlow() { setSpeed(0.4); m_isRunning = true; } public void runFast() { setSpeed(0.9); m_isRunning = true; } public void eject() { setSpeed(0.6); m_isRunning = true; } // same direction for now public void stop() { m_speed = 0.0; m_isRunning = false; } // now if clamping logic needs to change (e.g., to 0.0-0.8 for safety) // you ONLY change it in setSpeed() and everywhere else just works }
this. Called on an object: motor.setSpeed(0.5).return m_field;. Use isX() for boolean fields.spinUp(), stop(), deploy() — methods that DO something meaningful. Usually call setters internally.Topic 4 — Coding Prompt
Write a Climber class with three private fields:double m_extendSpeed, boolean m_isExtended, boolean m_isClimbing
Constructor: set all three to safe defaults — 0.0, false, false.
Methods:
• extend(double speed) — clamp speed to 0–1, set m_extendSpeed, set m_isExtended = true
• retract() — set speed to 0, m_isExtended = false
• startClimb() — ONLY sets m_isClimbing = true if m_isExtended is already true, otherwise do nothing
• stopClimb() — sets m_isClimbing = false
• getters for all three fields
Test: call extend(0.8), then startClimb(). Is m_isClimbing true? Now call retract() and try startClimb() again — it should NOT work.
Topic 4 — Quick Check
Live Class Builder
Fill in the Blanks
double m_motorSpeed;
public (int motorID) { }
Motor m =
.m_speed = speed;
Knowledge Check
Coding Challenge
Write an Intake class with:
• private fields: int m_motorID, double m_speed, boolean m_isRunning
• constructor Intake(int motorID) — sets motorID, starts speed at 0, isRunning at false
• start(double speed) — sets m_speed and m_isRunning = true
• stop() — sets m_speed to 0, m_isRunning = false
• isRunning() and getSpeed() getters
In main, create two Intake objects. Start one, leave the other stopped. Print both — only one should show isRunning = true.
Create DriveSubsystem.java in your minibot-project folder — the heart of your MiniBot project. This models how a real FRC drivetrain subsystem is structured on team 2974. You're writing plain Java here (no WPILib needed) — just simulate the data and behavior.
- Private fields:
int[] m_motorIDs(4 motors: FL, FR, BL, BR),double m_currentSpeed,boolean m_isInverted - Constructor takes no parameters — initialize
m_motorIDsfrom yourConstants.DriveKvalues (use the IDs you defined in week 1) public void setSpeed(double speed)— clamp speed to [-1.0, 1.0] before storingpublic double getSpeed()— return current speedpublic void stop()— sets speed to 0public boolean isMoving()— returns true if |speed| > 0.01public String getStatusString()— returns a formatted string like "Drive: 0.75 m/s | MOVING"- Full Javadoc on the class and every public method
Weekly Test
covers everything from week 5. the big OOP week. score goes to the leads — try without looking back!!