Overloading and Overriding in Java
Polymorphism can be of 2 forms
- Static
- Dynamic
When different form of a single entity are resolved at compile time (early binding) such polymorphism is called static polymorphism. When different form of a single entity are resolved during runtime (late binding) such polymorphism is called dynamic polymorphism.
Overloading is an example of static polymorphism.
Method overloading
In a class, how many methods can you define with the same name? Many! In Java, you can define multiple method with the same name, provided the argument list differ from each other.
In other words, if you provide different types of arguments, different number of arguments, or both, then you can define multiple methods with the same name. This is called method overloading.
The rules are simple:
- Overloaded method must change the argument list.
- Overloaded method can change the return type.
- Overloaded method can change the
access
modifier. - Overloaded method can declare new or broader checked exception
- A method can be overloaded in the same class or in a subclass. In other words, if class A define a
go(int i)
method, then class B could definego(String s)
method without overriding the superclass that takesint
. So methods with same name but in different class can still be considered overloaded, if the subclass inherits one version of the method and then declares another overloaded version in its class definition.
Legal Overloads
Let’s look at a method we want to overload:
public void registerAtItCuties(int age, String name) { }
The following methods are legal overloads of the changeSize()
method
public void registerAtItCuties(int age, String name) { } public int registerAtItCuties(int age, Float money) { } public void registerAtItCuties(String name, Float money) throws IOException { }
Example
package com.javalatte.itcuties.overloadingoverriding; import java.io.IOException; public class MethodOverloading { public static void main(String[] JavaLatte) { MethodOverloading obj = new MethodOverloading(); obj.registerAtItCuties(30, "Charlie", 99999.00F); try { obj.registerAtItCuties("Charlie", 55555F); } catch (IOException e) { } obj.registerAtItCuties(30, "Charlie"); obj.registerAtItCuties(31, 111F); } public void registerAtItCuties(int age, String name, float money) { System.out.println("Register with " + name + " Age: " + age + " Money : " + money); } public void registerAtItCuties(int age, String name) { System.out.println("Register with " + name + " Age: " + age); } public int registerAtItCuties(int age, float money) { System.out.println("Register with Age: " + age + " Money : " + money); return 0; } public void registerAtItCuties(String name, Float money) throws IOException { System.out.println("Register with Name: " + name + " Money : " + money); } }
Constructor Overloading
A default constructor is useful for creating object with a default initialization value.When you want to initialize the objects with different arguments in different instantiations, you can pass them as the arguments to constructors. This is called constructor overloading.
Example
package com.javalatte.itcuties.overloadingoverriding; class Student { private final int age; private final String name; private final long id; public Student() { this.age = 0; this.name = "unknown"; this.id = 0; } Student(String name) { this.name = name; this.age = 18;//default value this.id = 0; //default value } Student(String name, int age) { this.name = name; this.age = age; this.id = 0;// default value } Student(String name, int age, long id) { this.name = name; this.age = age; this.id = id; } public int getAge() { return age; } public long getId() { return id; } public String getName() { return name; } } public class ConstructorOverloading { public static void main(String[] JavaLatte) { Student s1 = new Student(); System.out.println("Name: " + s1.getAge() + " Age: " + s1.getAge() + " Id: " + s1.getId()); Student s2 = new Student("Charlie"); System.out.println("Name: " + s2.getAge() + " Age: " + s2.getAge() + " Id: " + s2.getId()); Student s3 = new Student("Charlie", 32); System.out.println("Name: " + s3.getAge() + " Age: " + s3.getAge() + " Id: " + s3.getId()); Student s4 = new Student("Charlie", 32, 122); System.out.println("Name: " + s4.getAge() + " Age: " + s4.getAge() + " Id: " + s4.getId()); } }
Invoking Overloaded Methods
When you define overloaded methods, how does the compiler know which method to call? Can you guess the output of the code?
public static void testMethod (int val) public static void testMethod (short val) public static void testMethod (Object val) public static void testMethod (String val ) public static void main(String[] args) { byte b = 9; testMethod(b); // first call testMethod(9); // second call Integer i = 9; testMethod(i); // third call testMethod("9"); // fourth call }
Here is how the compiler resolved these calls
- In the first method call, the statement is
testMethod(b)
where the variable b is of type byte. There is notestMethod
definition that takes byte as an argument. The closest type (in size) isshort
type and notint
, so the compiler resolves the calltestMethod(b)
totestMethod(short val)
definition - In the second method call, the statement is
testMethod(9)
. The constant value9
is of typeint
. The closest match istestMethod(int)
, so the compiler resolves the calltestMethod(9)
totestMethod(int val)
definition. - The third method call is
testMethod(i)
, where the variablei
is of typeInteger
. There is notestMethod
definition that takesInteger
as an argument. The closest match istestMethod(Object val)
, so it is called. Why nottestMethod(int val)
? For finding the closest match, the compiler allows implicit upcasts, not downcasts, sotestMethod(int val)
is not considered - The last method call is
testMethod("9")
. The argument is aString
type. Since there is an exact match,testMethod(String val)
is called.
For resolving a method call, it first looks for the exact match – the method definition with exactly same number of parameters and types of parameters. If it can’t find an exact match, it looks for the closest match by using upcasts. If the compiler can’t find any match, then you’ll get a compiler error.
Example
package com.javalatte.itcuties.overloadingoverriding; public class InvokingOverloadedMethod { public static void main(String[] JavaLatte) { byte b = 9; testMethod(b); // first call testMethod(9); // second call Integer i = 9; testMethod(i); // third call testMethod("9"); // fourth call } public static void testMethod(int val) { System.out.println("int"); } public static void testMethod(short val) { System.out.println("short"); } public static void testMethod(Object val) { System.out.println("object"); } public static void testMethod(String val) { System.out.println("String"); } }
Here are some interesting rules regarding method overloading
- You cannot overload methods with the methods differing in return types alone.
- You cannot overload methods with the methods differing in exception specifications alone.
- For overload resolution to succeed, you need to define methods such that the compiler finds one exact match. If the compiler finds no matches for your call or if the matching is ambiguous, the overload resolution fails and the compiler issues an error.
Overloading is an example of dynamic/runtime polymorphism
Method Overriding
Any time you have a class that inherits a method from a superclass, you have the opportunity to override the method unless it is marked final. The key benefit of overriding is the ability to define behaviour that’s specific to a particular class.
Overriding i.e, runtime polymorphism is a simple yet powerful idea for extending functionality.
For abstract methods you inherit from a superclass, you have no choice. You must implement the method in the subclass unless the subclass is also abstract. Abstract methods must be implemented by the concrete subclass, but this is a lot like saying that the concrete subclass overrides the abstract methods of the superclass. So you could think of abstract methods as methods you’re forced to override.
The rules are simple:
- The argument list must exactly match that of the overridden method. If they don’t match, you can end up with an overloaded method you didn’t intend.
- The return type must be the same as, or a subtype of, the return type declared in the original overridden method in the superclass.
- The access level can’t be more restrictive than the overridden method’s.
- The access level CAN be less restrictive than that of the overridden method.
- Instance methods can be overridden only if they are inherited by the subclass. A subclass within the same package as the instance’s superclass can override any superclass method that is not marked private or final. A subclass in a different package can override only those non-final methods marked public or protected.
- The overriding method CAN throw any unchecked (runtime) exception, regardless of whether the overridden method declares the exception
- The overriding method must NOT throw checked exceptions that are new or broader than those declared by the overridden method.
- You cannot override a method marked final.
- You cannot override a method marked static.
- If a method can’t be inherited, you cannot override it.
Example
package com.javalatte.itcuties.overloadingoverriding; class Animal { public void eat() { System.out.println("Generic eating method"); } public void run() { System.out.println("Generic run"); } } class Horse extends Animal { @Override public void eat() { System.out.println("Horse eating method"); } //You cannot have more restrictive access modifer than the method being //overriden //private void run(){ } } public class MethodOverriding { public static void main(String[] JavaLatte) { Animal a = new Animal(); Animal a1 = new Horse(); a.eat(); // animal eat() a1.eat(); // horse eat() } }
Polymorphically, when someone has an Animal
reference that refers not to an Animal
instance, but to an Animal
subclass instance, the caller should be able to invoke eat()
on the Animal
reference, but the actual runtime object (say, a Horse
instance) will run its own specific eat()
method.
To reiterate, the compiler looks only at the reference type, not the instance type. Polymorphism lets you use a more abstract supertype (including an interface) reference to refer to one of its subtypes (including interface implementers).
Invoking a Superclass Version of an Overridden Method
You’ll want to take advantage of some of the code in the superclass version of a method, yet still override it to provide some additional specific behavior.
It’s like saying, “Run the superclass version of the method, then come back down here and finish with my subclass additional method code.”
Example
package com.javalatte.itcuties.overloadingoverriding; class Mammel { public void eat() { } public void printing() { System.out.println("Useful code for printing"); } } class cat extends Mammel { @Override public void printing() { //taking advantage of super class method super.printing(); System.out.println("Useful code for printing for cat Class"); } } public class InvokeOverridenMethod { public static void main(String[] JavaLatte) { Mammel m = new cat(); m.printing(); } }
Examples of Legal and Illegal Method Overrides
public class Animal { public void run() { } }
private void run() { }
: Access modifier is more restrictive
public void run() throws IOException { }
: Declares a checked exception not defined by superclass version
public void run(String food) { }
: A legal overload, not an override, because the argument list changed
public String run() { }
: Not an override because of the return type, not an overload either because there’s no change in the argument list
Difference between Overloaded and Overridden method
Overloaded Method | Overridden Method | |
Arguments | Must change | Must not change |
Return type | Can change | Can’t change |
Exceptions | Can change | Can reduce or eliminate.Must not throw new or broader checked exceptions. |
Access | Can change | Must not make more restrictive (can be less restrictive). |
Invocation | Reference type determines which overloaded version (based on declared argument types) is selected. Happens at compile time. | Object type (in other words, the type of the actual instance on the heap) determines which method is selected. Happens at runtime. |
Download this sample code here.
this is really very useful post about method overriding and overloading in java.
example is also very nice and clean.
thank you so much for this post
this is rally very nice and useful post about overloading and overriding in java
thanks for this post
Method overloading is not possible by changing the return type of the method because there may occur ambiguity. your site content are really awesome , thanks for sharing this.