What overriding means
A child class inherits its parent's methods. Overriding is what happens when the child wants to change one of them: it declares a method with the same name and parameters as the parent's, and the child's version is used for child objects.
Subclasses are special cases of the parent. A Puppy is a Dog, but it sounds different. Rather than inventing a new method name, you redefine bark() in Puppy. Code that calls bark() doesn't need to know which kind of dog it holds.
The syntax
class Dog {
public void bark() { System.out.println("Woof!"); }
}
class Puppy extends Dog {
@Override
public void bark() { System.out.println("Yip!"); }
}
Java decides which version to run by the object's real class. Given a Puppy object, bark() prints Yip!. Given a plain Dog, it prints Woof!. Java needs no special keyword on the parent: every method is overridable by default unless it is final, static or private.
The @Override annotation
@Override is optional, but you should always write it. It tells the compiler "this method is meant to replace one from a parent", and the compiler then checks that a matching parent method exists. Without it, a small slip creates a brand new method and no error appears. With it, the slip is caught right away:
class Puppy extends Dog {
@Override
public void bark(int times) { }
}
The compiler rejects this with method does not override or implement a method from a supertype. The parent's method is bark() with no parameters, and bark(int times) has a different signature, so it would be a separate overload and not an override. A misspelling such as barc() triggers the same error.
The rules for a valid override
- The name and the parameter types must match the parent's method exactly.
- The return type must be the same, or a subtype of it for object types.
- The access level cannot be narrower. A parent's
publicmethod cannot be overridden asprivateor package-private. - Methods marked
finalcannot be overridden at all.
Reaching the parent's version
An override can still use the code it replaced. Writing super.bark() inside the child runs the parent's version on the same object:
class Puppy extends Dog {
public Puppy(String name) { super(name); }
@Override
public void bark() {
super.bark();
System.out.println("(squeaky)");
}
}
For a puppy named Buddy, this prints Woof! I am Buddy followed by (squeaky). That keeps the parent's behavior and adds to it, without copying its code.
The lesson's exercise, step by step
The starter code gives Dog a bark that prints Woof! I am and the name, and gives Puppy only a constructor. Two steps change the puppy's sound:
- Declare the override. Inside
Puppy, add@Overrideandpublic void bark(), with the same name, no parameters and the samevoidreturn type as the parent. - Fill in the body. Print
"Yip! I am "followed byname. The field is inherited fromDog, and it is public, soPuppycan use it by its bare name.
The complete program:
class Dog {
public String name;
public Dog(String name) { this.name = name; }
public void bark() {
System.out.println("Woof! I am " + name);
}
}
class Puppy extends Dog {
public Puppy(String name) { super(name); }
@Override
public void bark() {
System.out.println("Yip! I am " + name);
}
}
class Solution {
public void mainFunction() {
Puppy buddy = new Puppy("Buddy");
buddy.bark();
}
}
The output is:
Yip! I am Buddy
Because the variable holds a Puppy, Java runs the version in Puppy. Dogs that are not puppies still print Woof! I am ....
The chosen version depends on the object
The version of a method that runs depends on the object, not on the variable's declared type. If you assign a puppy to a Dog variable, the puppy still barks like a puppy:
Dog d = new Puppy("Buddy");
d.bark();
This prints Yip! I am Buddy. The next lesson, on polymorphism, makes use of exactly this behavior.
Overriding and overloading
The two words sound alike but mean different things. Overriding replaces an inherited method in a subclass, using the same signature. Overloading puts several methods with the same name but different parameter lists in the same class. @Override is correct only for the first.
Common mistakes
- Changing the parameter list. Adding or changing a parameter creates an overload, not an override.
- Leaving out
@Override. The code compiles even when you misspell the method name, and the parent's version silently keeps running. - Weakening the access. Writing
void bark()with no modifier in the child, when the parent's ispublic, is a compile error. - Declaring a second
namefield in the child. Both versions ofbarkread the same inherited field. A new field inPuppywould hide it, and the constructor'ssuper(name)would no longer fill the one the child uses. - Expecting static methods to be overridden. Static methods are hidden, not overridden, and they don't follow the object's real class.
Key takeaways
- A child overrides a parent method by declaring one with the same name and parameters.
- Mark overrides with
@Override, so the compiler catches signature mistakes. - Java picks the version using the object's actual class at runtime.
super.method()calls the parent's version from inside an override.- The access level of an override may stay the same or become wider, but never narrower.