ZetCode

Java @Override Annotation

Last modified: April 13, 2025

The @Override annotation is a marker annotation that indicates a method is intended to override a method in a superclass. It helps catch errors at compile time when the method doesn't actually override anything.

Using @Override improves code readability and helps prevent common mistakes in method overriding. The compiler will generate an error if the annotated method doesn't override a method from a superclass or interface.

Basic Usage of @Override

The simplest use of @Override is when overriding methods from a parent class. The annotation goes immediately before the method declaration. This ensures you're actually overriding a method as intended.

Main.java
package com.zetcode;

class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Dog barks");
    }
}

public class Main {

    public static void main(String[] args) {
        Animal myDog = new Dog();
        myDog.makeSound(); // Outputs: Dog barks
    }
}

In this example, the Dog class overrides the makeSound method from Animal. The @Override annotation confirms this is an intentional override. If we misspelled the method name, the compiler would catch it.

Interface Implementation with @Override

@Override can also be used when implementing interface methods. This helps ensure you're correctly implementing all required interface methods. The compiler will verify the method signatures match.

Main.java
package com.zetcode;

interface Shape {
    double calculateArea();
    String getName();
}

class Circle implements Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public String getName() {
        return "Circle";
    }
}

public class Main {

    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        System.out.println(circle.getName() + " area: " + 
                         circle.calculateArea());
    }
}

Here, Circle implements the Shape interface. Both interface methods are marked with @Override. If we changed a method signature, the compiler would flag it as not matching the interface.

Detecting Mistakes with @Override

One key benefit of @Override is catching mistakes early. If you think you're overriding a method but actually aren't, the compiler will warn you. This prevents subtle bugs from creeping into your code.

Main.java
package com.zetcode;

class Vehicle {
    public void startEngine() {
        System.out.println("Engine started");
    }
}

class Car extends Vehicle {
    @Override
    public void startEngin() { // Misspelled method name
        System.out.println("Car engine started");
    }
}

public class Main {

    public static void main(String[] args) {
        Vehicle myCar = new Car();
        myCar.startEngine(); // Calls Vehicle's method
    }
}

In this example, we intended to override startEngine but misspelled it as startEngin. The @Override annotation causes the compiler to flag this as an error. Without it, the code would compile but behave unexpectedly.

Overriding Object Class Methods

Common methods from Object like toString, equals, and hashCode should always use @Override. This ensures proper overriding of these fundamental methods.

Main.java
package com.zetcode;

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && name.equals(person.name);
    }
    
    @Override
    public int hashCode() {
        return 31 * name.hashCode() + age;
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class Main {

    public static void main(String[] args) {
        Person p1 = new Person("Alice", 30);
        Person p2 = new Person("Alice", 30);
        
        System.out.println(p1);
        System.out.println("p1 equals p2: " + p1.equals(p2));
    }
}

This example shows proper overriding of three key Object methods. Each uses @Override to ensure correct signatures. The equals method demonstrates proper type checking and field comparison.

Abstract Class Method Overriding

When extending abstract classes, @Override should be used for all concrete implementations of abstract methods. This clarifies the code's intent and helps catch signature mismatches.

Main.java
package com.zetcode;

abstract class Shape {
    public abstract double calculateArea();
    public abstract void draw();
    public void describe() {
        System.out.println("This is a shape");
    }
}

class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }

    @Override
    public void draw() {
        System.out.println("Drawing a circle with radius " + radius);
    }

    @Override
    public void describe() {
        System.out.println("This is a circle with radius " + radius);
    }
}

public class Main {

    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        circle.draw();
        System.out.println("Area: " + circle.calculateArea());
        circle.describe();
    }
}

Here, Circle implements both abstract methods and overrides a concrete method from Shape. All overrides are marked with @Override. The compiler ensures each method correctly matches its parent declaration.

Overriding Methods from Generic Classes

When working with generic classes, @Override helps ensure type safety in method overrides. The annotation verifies method signatures including generic type parameters.

Main.java
package com.zetcode;

class Box<T> {
    private T content;
    
    public void setContent(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

class StringBox extends Box<String> {
    @Override
    public void setContent(String content) {
        if (content == null || content.isEmpty()) {
            throw new IllegalArgumentException("Content cannot be empty");
        }
        super.setContent(content);
    }
    
    @Override
    public String getContent() {
        String content = super.getContent();
        return content != null ? content.toUpperCase() : null;
    }
}

public class Main {

    public static void main(String[] args) {
        StringBox box = new StringBox();
        box.setContent("hello");
        System.out.println(box.getContent()); // Outputs: HELLO
    }
}

In this example, StringBox extends Box<String> and overrides its methods. The @Override annotations confirm we're correctly overriding the generic methods with specific String implementations. The compiler checks type compatibility.

Default Interface Methods and @Override

Java 8 introduced default methods in interfaces. When overriding these default methods, @Override should be used to make the intention clear and catch potential errors.

Main.java
package com.zetcode;

interface Logger {
    default void log(String message) {
        System.out.println("Default log: " + message);
    }
    
    void error(String message);
}

class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("File log: " + message);
    }
    
    @Override
    public void error(String message) {
        System.out.println("File error: " + message.toUpperCase());
    }
}

public class Main {

    public static void main(String[] args) {
        Logger logger = new FileLogger();
        logger.log("test message");
        logger.error("something went wrong");
    }
}

This example shows a FileLogger that overrides both a default method (log) and an abstract method (error) from the Logger interface. The @Override annotations make it clear which methods are being overridden and ensure correct signatures.

Source

Java @Override Annotation Documentation

This tutorial has covered all key aspects of the @Override annotation with practical examples. Proper use of @Override makes your code more robust, readable, and maintainable by catching errors early.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.