-
Method overriding is only possible for methods that are inheritable.
For example, a method marked private
is not inheritable and therefore cannot be overridden.
However, you can define a method in the subclass with the same name, signature (parameters), and return type, but in this case, it's treated as a new method completely unrelated to the one in the superclass.
-
A subclass in the same package as the superclass can override any method that is not marked
private
or final
.
-
A subclass in a different package can override only those methods marked
public
or protected
(and not marked final
).
-
You cannot override a method marked
final
.
The compiler will show an error: "Cannot override the final method from SuperClass"
public class SuperClass {
public final Integer subtractOperation(Integer value1, Integer value2) {
return (value1 - value2);
}
}
public class SubClass extends SuperClass {
public Integer subtractOperation(Integer value1, Integer value2) { // compiler error: Cannot override the final method from SuperClass
return (value1 - value2) * 10;
}
}
-
You cannot override a method marked
static
.
The compiler will show an error: "This instance method cannot override the static method from SuperClass"
public class SuperClass {
public static Integer subtractOperation(Integer value1, Integer value2) {
return (value1 - value2);
}
}
public class SubClass extends SuperClass {
public Integer subtractOperation(Integer value1, Integer value2) { // compiler error: This instance method cannot override the static method from SuperClass
return (value1 - value2) * 10;
}
}
However, it is valid to declare the subclass method as static
too; in this case, it is a new method that hides the superclass method (method hiding, not overriding).
When both methods are static, the method version called depends on the declared type of the variable, not the actual object instance.
public class SubClass extends SuperClass {
public static Integer subtractOperation(Integer value1, Integer value2) {
return (value1 - value2) * 10;
}
}
public class TestClass {
public static void main(String[] args) {
SuperClass superClass1 = new SuperClass();
SuperClass superClass2 = new SubClass();
SubClass subClass1 = new SubClass();
System.out.println(superClass1.subtractOperation(5, 2)); // prints 3: uses SuperClass version
System.out.println(superClass2.subtractOperation(5, 2)); // prints 3: uses SuperClass version
System.out.println(subClass1.subtractOperation(5, 2)); // prints 30: uses SubClass version
System.out.println();
System.out.println(SuperClass.subtractOperation(5, 2)); // prints 3
System.out.println(SubClass.subtractOperation(5, 2)); // prints 30
}
}
-
To be considered an overriding method, the method signature must exactly match that of the superclass method.
The method signature includes the method name and parameter list (number, types, and order of parameters).
Each parameter in the subclass method must be of the same type as the corresponding parameter in the superclass method.
If the number of parameters is different or their types differ (even if it's a subtype), it is not considered overriding but method overloading (a completely new method).
public class SuperClass {
public void doSomething(SuperClass superClass) { }
}
public class SubClass extends SuperClass {
public void doSomething(SuperClass superClass) { } // overriding
public void doSomething(SubClass subClass) { } // overloading (new method)
public void doSomething(SubClass subClass1, SubClass subClass2) { } // overloading (new method)
}
Details about method overloading will be discussed on a separate page (Method Overloading),
but for now, note that the method selected at compile time depends on the argument types.
public class TestClass {
public static void main(String[] args) {
SuperClass superClass1 = new SuperClass();
SuperClass superClass2 = new SubClass();
SubClass subClass1 = new SubClass();
superClass1.doSomething(superClass1); // calls SuperClass version
superClass1.doSomething(subClass1); // calls SuperClass version
superClass2.doSomething(superClass1); // calls SubClass version
superClass2.doSomething(subClass1); // calls SubClass version
subClass1.doSomething(superClass1); // calls SubClass version
subClass1.doSomething(subClass1); // calls overloaded method in SubClass (SubClass parameter)
subClass1.doSomething(subClass1, subClass1); // calls another overloaded method in SubClass
}
}
-
The return type of the overriding method must be the same or a subtype of the return type of the superclass method (covariant return type).
If the return type is incompatible, the compiler will throw an error.
public class SuperClass {
public SuperClass doSomething() { return null; }
}
public class SubClass extends SuperClass {
public Object doSomething() { return null; } // compiler error: The return type is incompatible with SuperClass.doSomething()
}
To fix this, the return type should be changed to either SuperClass
or SubClass
.
Note that if the parameter lists differ, this rule does not apply since it's not considered overriding but overloading.
-
The visibility (access modifier) of the overriding method cannot be more restrictive than that of the superclass method.
For example, you cannot override a public
method and declare it as protected
or private
:
public class SuperClass {
public void doSomething() { }
}
public class SubClass extends SuperClass {
protected void doSomething() { } // compiler error: Cannot reduce the visibility of the inherited method from SuperClass
}
-
The visibility of the overriding method can be less restrictive (more accessible).
For example, you can override a protected
method and declare it as public
:
public class SuperClass {
protected void doSomething() { }
}
public class SubClass extends SuperClass {
public void doSomething() { }
}
-
The overriding method can throw any unchecked exception (runtime exceptions and errors),
regardless of whether the superclass method throws an exception or not.
public class SuperClass {
public void doSomething() { }
}
public class SubClass extends SuperClass {
public void doSomething() throws RuntimeException { }
}
-
The overriding method cannot throw checked exceptions that are not declared by the superclass method.
public class SuperClass {
public void doSomething() { }
}
public class SubClass extends SuperClass {
public void doSomething() throws FileNotFoundException { } // compiler error: Exception FileNotFoundException is not compatible with throws clause in SuperClass.doSomething()
}
-
The overriding method may choose not to throw checked exceptions that the superclass method declares.
public class SuperClass {
public void doSomething() throws FileNotFoundException { }
}
public class SubClass extends SuperClass {
public void doSomething() { }
}
Note that the compiler only considers the declared type of the variable
when determining whether to require a try-catch
block.
public class TestClass {
public static void main(String[] args) {
SuperClass superClass1 = new SuperClass();
SuperClass superClass2 = new SubClass();
SubClass subClass1 = new SubClass();
superClass1.doSomething(); // compiler error: Unhandled exception type FileNotFoundException
superClass2.doSomething(); // compiler error: Unhandled exception type FileNotFoundException
subClass1.doSomething(); // OK
}
}
-
The overriding method can only throw checked exceptions that are the same or subtypes of those thrown by the superclass method.
public class SuperClass {
public void doSomething() throws IOException { }
}
public class SubClass extends SuperClass {
public void doSomething() throws FileNotFoundException, ZipException { }
}
Otherwise, the compiler will display an error:
public class SuperClass {
public void doSomething() throws FileNotFoundException { }
}
public class SubClass extends SuperClass {
public void doSomething() throws IOException { } // compiler error: Exception IOException is not compatible with throws clause in SuperClass.doSomething()
}