Skip to main content

Lesson 13.3: Overriding Behavior with @Override for Custom Implementations


Why the Same Method Name Needs Different Behavior

Inheritance establishes that child classes are specialized versions of their parent. Sometimes that specialization means doing the same thing slightly differently. A BaseMotorMechanism parent might have a stop() method that calls motor.setPower(0). For most mechanisms, that is sufficient. For a high-reach scoring lift that holds a heavy game element, a bare setPower(0) is not safe -- it allows gravity to drop the element the instant the driver releases the joystick. That specific mechanism needs a stop() that also configures BRAKE behavior before cutting power.

The mechanism still needs to be called stop() -- it would be confusing to name it anything else, and external code that calls stop() on any mechanism should not need to know which specific mechanism it is talking to. The solution is method overriding: the child class redefines the method with the same name and signature, replacing the parent's implementation with its own.


The @Override Annotation

The @Override annotation is placed on the line immediately above a method in a child class to signal that the method is intentionally replacing a version inherited from the parent. Technically the annotation is optional -- the override will happen whether or not it is present. In practice, omitting it is an engineering mistake.

The annotation serves two purposes. First, it tells the compiler to verify that the parent class actually has a method with that exact name and parameter types. If you misspell the method name or get the parameter types wrong, the compiler will catch it and report an error. Without @Override, the misspelled method would silently compile as a brand new method rather than an override, and the parent's original version would continue to run.

Second, it documents intent for every other person who reads the code. When a teammate sees @Override, they immediately understand that this method exists in the parent and that this class is deliberately changing its behavior.


Abstract Methods

When a parent class defines a method as abstract, it declares that the method exists and specifies its signature, but provides no implementation body. The parent is saying: every child of mine must implement this method, but I cannot provide a meaningful default. Any class that contains at least one abstract method must itself be declared abstract, and an abstract class cannot be instantiated directly. Only concrete child classes that implement all abstract methods can be instantiated.

In FTC, abstract classes are useful for defining a common interface for a family of mechanisms. An abstract class Shooter might declare abstract void fire() because different shooter designs require completely different firing logic, but all of them need a fire() method that autonomous code can call by name.


Annotated Code

package org.firstinspires.ftc.teamcode.mechanisms;

import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.HardwareMap;

/** Generic motor mechanism with a basic stop implementation. */
public class BaseMotorMechanism {

protected DcMotor motor;

public void init(HardwareMap hwMap, String motorName) {
motor = hwMap.get(DcMotor.class, motorName);
motor.setPower(0);
}

public void stop() {
motor.setPower(0); // default: just cut voltage
}
}
package org.firstinspires.ftc.teamcode.mechanisms;

import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.HardwareMap;

/**
* A lift that holds position against gravity when stopped.
* Overrides stop() to configure BRAKE mode before cutting power.
*/
public class HeavyLiftMechanism extends BaseMotorMechanism {

@Override
public void init(HardwareMap hwMap, String motorName) {
super.init(hwMap, motorName);
motor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
}

public void extend() {
motor.setPower(0.75);
}

/**
* Overrides the parent's stop() with a safer version that explicitly
* engages BRAKE mode before removing voltage.
*/
@Override
public void stop() {
motor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
motor.setPower(0); // BRAKE is now active at zero power
}
}
package org.firstinspires.ftc.teamcode.mechanisms;

import com.qualcomm.robotcore.hardware.HardwareMap;

/**
* Abstract class demonstrating that child classes must provide
* their own fire() implementation.
*/
public abstract class BaseShooter {

/** Every shooter must implement this -- no default behavior is possible. */
public abstract void fire();

/** Common behavior that all shooters share can still be defined here. */
public void idle() {
// common idle logic
}
}

Fill-in-the-Blank Practice

  1. The annotation placed immediately above a child class method to signal it is replacing an inherited version is __________.
  2. Without the @Override annotation, a misspelled method name in a child class would compile silently as a __________ method rather than reporting an error.
  3. A method declared with the abstract keyword in a parent class has no __________ and requires every non-abstract child class to provide one.
Show answers
  1. @Override
  2. new (brand new unrelated method)
  3. body (implementation)

Template Challenge

Robot Scenario: A GenericSlide class has a move(double power) method that passes power directly to a motor. Write a SafetySlide child class that overrides move() to enforce a maximum power of 0.5 regardless of what value is passed in. Use Math.min() and Math.abs() with Math.signum() to scale the input while preserving direction.

package org.firstinspires.ftc.teamcode.mechanisms;

import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.HardwareMap;

// Parent class -- do not modify
public class GenericSlide {
protected DcMotor motor;

public void init(HardwareMap hwMap, String name) {
motor = hwMap.get(DcMotor.class, name);
}

public void move(double power) {
motor.setPower(power);
}
}
package org.firstinspires.ftc.teamcode.mechanisms;

public class SafetySlide extends GenericSlide {

private static final double MAX_POWER = 0.5;

// INSERT CODE HERE: Override move() so that the absolute value of power
// is capped at MAX_POWER while preserving the original direction.
// Hint: scale the input as Math.min(Math.abs(power), MAX_POWER) * Math.signum(power)

}
Show answer
@Override
public void move(double power) {
double safePower = Math.min(Math.abs(power), MAX_POWER) * Math.signum(power);
motor.setPower(safePower);
}

Ready to move on?

Sign in with Google to save your progress with Telemark, or continue without saving.