Unit 13 Mastery Quiz: OOP & Inheritance
I. Conceptual Questions
1. Encapsulation vs. Accessibility: Why is it considered a best practice to mark a mechanism's hardware objects (such as a DcMotor) as private and provide public control methods instead of making the hardware objects public directly?
Show answer
Making the hardware object public allows any OpMode to call arbitrary SDK methods on it directly -- setting encoder modes mid-match, changing direction unexpectedly, or resetting run modes in ways the mechanism designer did not intend. Marking it private creates a boundary. External code can only interact with the mechanism through the public methods, which means all hardware access is routed through the mechanism's own logic where invariants, safety checks, and correct state transitions can be enforced. It also means the internal wiring can change -- for example, swapping a motor for a different model on a different port -- without any OpMode needing to be updated.
2. Inheritance vs. Composition: Your team is building a CompetitionRobot class that coordinates a Drivetrain, a Lift, and an Intake. Should CompetitionRobot extend Drivetrain or should it hold a Drivetrain as a member variable? Explain why.
Show answer
CompetitionRobot should hold a Drivetrain as a member variable. This is the composition ("has-a") relationship. Inheritance ("is-a") would be appropriate only if CompetitionRobot were a more specialized version of Drivetrain -- which it is not. A competition robot is not a type of drivetrain; it is an assembly that includes a drivetrain among other subsystems. Using inheritance here would incorrectly imply that every method on Drivetrain is also a method on CompetitionRobot, and it would prevent the robot class from also inheriting from a Lift since Java does not allow multiple inheritance. Composition is the correct model because the robot owns its subsystems rather than being a specialized version of any one of them.
3. The Value of @Override: A student writes a stop() method in a child class without the @Override annotation and accidentally misspells it as stopp(). The code compiles without errors. What will happen at runtime, and how would the annotation have prevented the problem?
Show answer
At runtime, any code that calls stop() on an instance of the child class will invoke the parent's stop() method, because stopp() is a completely separate new method that nothing calls. The intended custom behavior in stopp() never executes. The @Override annotation instructs the compiler to verify that the parent class contains a method with the exact same name and parameter types. If the name is misspelled or the parameters do not match, the compiler reports an error before the code can even be built. With the annotation present, stopp() would have been caught at compile time rather than failing silently at runtime.
4. Abstract Classes and Forced Implementation: Why would a lead programmer declare a class as abstract and mark a specific method as abstract rather than providing a default implementation in the parent?
Show answer
An abstract method declaration is appropriate when there is no meaningful default implementation that all child classes could share. Providing an empty or placeholder body would allow child classes to be instantiated without overriding the method, silently doing nothing when it is called. Declaring the method abstract makes the absence of an implementation an explicit contract: any concrete class that extends this parent is required by the compiler to provide its own version. This is particularly useful for mechanism families where the interface is fixed -- every shooter must have a fire() method -- but the implementation is entirely different depending on the design.
5. Configuration Drift: A team has five OpMode files and three mechanism classes, each containing the literal string "left_drive" to map the left drivetrain motor. The motor port name changes before a competition. Describe the risk this creates and explain how a static final constant class eliminates it.
Show answer
With the string literal duplicated in eight places, each file must be found and edited individually. Under competition time pressure, one or more will be missed. The files that were missed will throw a NullPointerException when the hardware map fails to find a device named "left_drive", crashing those OpModes during the initialization phase. A static final String LEFT_DRIVE = "left_drive" in a shared RobotConfig class means the string exists in exactly one location. All eight files import RobotConfig and reference RobotConfig.LEFT_DRIVE. Changing the value requires editing one file, and all eight files receive the updated value automatically on the next build.
II. Debug the Code
The following code is intended to create a CompetitionRobot class and use it in a TeleOp to drive the robot. Identify and fix the 2 errors.
// CompetitionRobot.java
package org.firstinspires.ftc.teamcode.mechanisms;
import com.qualcomm.robotcore.hardware.HardwareMap;
public class CompetitionRobot {
public MecanumDrivetrain drive; // declared but never instantiated
public CompetitionRobot() {
// Error 1: drive is never assigned here
}
public void init(HardwareMap hwMap) {
drive.init(hwMap);
}
}
// ModularTeleOp.java
package org.firstinspires.ftc.teamcode;
import com.qualcomm.robotcore.eventloop.opmode.OpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import org.firstinspires.ftc.teamcode.mechanisms.CompetitionRobot;
@TeleOp(name="Modular_Debug")
public class ModularTeleOp extends OpMode {
CompetitionRobot robot = new CompetitionRobot();
@Override
public void init() {
robot.init(hardwareMap);
}
@Override
public void loop() {
// Error 2: attempting to call drive() directly on the robot object
robot.drive(
-gamepad1.left_stick_y,
gamepad1.left_stick_x,
gamepad1.right_stick_x
);
}
}
Show answers
Error 1 -- Instantiation Error:
The drive member variable is declared as public MecanumDrivetrain drive; but is never assigned a value. When init() is called and tries to invoke drive.init(hwMap), the reference is null and a NullPointerException is thrown. The fix is to instantiate the object either at declaration or inside the constructor:
public MecanumDrivetrain drive = new MecanumDrivetrain();
Error 2 -- Method Access Error:
drive() is a method on the MecanumDrivetrain class, not on CompetitionRobot itself. The robot class does not have a drive() method -- it has a drive member that is a MecanumDrivetrain object. The correct call uses dot notation to reach through the member to the subsystem's method:
robot.drive.driveFieldCentric(
-gamepad1.left_stick_y,
gamepad1.left_stick_x,
gamepad1.right_stick_x
);
Ready to move on?
Sign in with Google to save your progress with Telemark, or continue without saving.