Java deals with classes and objects. Each object has some specific task/operation to perform, this is done with methods.
A method is a set of instructions that performs a specific task. In Java, the main() method is the first method executed by the compiler.
The signature of a method comprises the method name and the parameter list. E.g., consider a method named addition that performs an addition operation:
public int addition (int first, int second) {
return (first + second);
}
//highlighted portion is the signature of the addition metho
Method declaration
NOTE: The return type of the method is not considered in the signature of a method.
Java language has a ‘feature’ called method overloading which allows multiple methods within a class to have the same name with different argument lists.
It is a type of polymorphism (the process by which we can perform a single task in various ways). To be more concise, overloading is an example of “static polymorphism”.
E.g., consider the addition method again, but this time you are not sure whether you will have to add three integers or two integers:
int addition (int first , int second) {
return (first + second);
}
int addition (int first , int second , int third) {
return (first + second + third);
}
It is all done by the compiler. Whenever we call the overloaded method, the compiler looks for the method having the same name and the same type of argument list i.e., same signature, and calls that method. E.g., consider the addition method with the following snippet:
//...defining overloaded addition method as before...
//...calling addition method
int sumTwo = addition (6,9);
//compiler calls addition (int,int)
int sumThree = addition (6,0,9);
//compiler calls addition (int,int,int)
NOTE: Since the decision of invoking the correct method is taken ‘statically’ at compile time, overloading is an example of static polymorphism.
The following points must be critically considered while using overloaded methods. Each overloaded method must differ in terms of the argument list. There are three basic ways to make different argument lists :
By having a different number of parameters
int addition (int first , int second) {
//Implementation code
}
int addition (int first , int second , int third) {
//Implementation code
}
By having different types of parameters
int addition (int first , int second) {
//Implementation code
}
float addition (float first , float second , float third) {
//Implementation code
}
//note that we have different number of paramters as well
By having different order of the parameters in the argument list
String addition (int first , String second) {
//logic
}
String addition (String first , int second) {
//logic
}
By combining the above-mentioned ways also, we can make different argument lists.
Some additional notes
Think of a scenario where you don’t overload addition() and have different methods for each kind of addition, i.e.,
int addTwo (int first , int second) {
//Implementation code
}
int addThree (int first , int second , int third) {
//Implementation code
}
String addStringNum (int first , String second) {
//Implementation code
}
The main purpose of overloading is to improve the readability of code by using the same name for overloaded methods. The above-written code is not optimal in terms of readability as each method is having a different name but in the end, the task performed by each method is the same. In our example, the task was to add numbers (represented by strings also).
Therefore, method overloading improves code readability and makes programming easier.
Consider the following definition of work() method and call to work():
void work (long num) {
System.out.println(num);
}
//...
//calling work() with an int value
int test = 0;
work (test);
This code runs without any error because the compiler performs an implicit type promotion on the variable test. One can refer to this chart for type promotion in Java:
Things get interesting when we consider type promotion in overloaded methods. Let’s see.
If the compiler can’t find an exact match of invoked method (based on the argument list), it will promote the lower data type argument and again check for a match. If there’s a match, that method will be used otherwise; it will promote the argument to the next higher data type in the promotion chain and keeps checking. This process is continued until the chain completes or the compiler gets a match.
In the end, if the chain is completed, the compiler will report a compile-time error!
Example 1:
public class TypePromo{
public void Method(long first) {
System.out.println("Long "+ first);
}
public void Method(float first) {
System.out.println("Float "+ first);
}
public static void main(String args[]) {
TypePromo obj = new TypePromo();
obj.Method(21);
}
}
//int gets promoted to long
Output: Long 21
Example 2: (Ambiguity error in method overloading)
public class TypePromo{
public void Method(int first, double second) {
System.out.println(first + second);
}
public void Method(double first, int second) {
System.out.println(first + second);
}
public static void main(String args[]) {
TypePromo obj = new TypePromo();
obj.Method(14 , 14);
}
}
This code results in a compilation error —
The method Method (int, double) is ambiguous for the type TypePromo
This is because both types of Method() are applicable after type promotion which arises an ambiguity of which method to invoke. This is the ambiguity error.
In this blog, we gained quality knowledge about methods in Java, starting with what methods are which was followed by the signature of a method. Then we explored method overloading, its application, benefits, and various ways of implementation along with examples. At last, we saw how type-promotion affects method overloading.
Encapsulation is one of the fundamental concepts in OOP that bundles data and associated methods that operate on that data into a single block called class. It is also a pathway for restricting direct access to some data and methods associated with a class (which leads to data hiding).
There might be some objects common to all parts of the program, and we require a single instance of it. The Singleton is a creational design pattern that ensures that a class has only one instance throughout the application. This instance is accessible from any part of the application with global access.
To write clear and maintainable code, software engineering principles are recommendations that programmers should follow during software development. It is a set of approaches and best practices introduced by some famous industry experts and authors. This blog will go through some popular software engineering principles for developing quality software.
In OOPS, Abstraction exposes necessary functionality to external objects and hides implementation details. This helps programmers to use complex logic without understanding its implementation. In other words, Abstraction is a process of exposing relevant functionalities so that one needs to know what the code does, not how it does it.
Object-Oriented Programming binds together the data and the methods in the form of an object and selectively exposes the data to other objects. It primarily revolves around classes and objects, i.e., their definition, instantiation, relationship, communication, etc. It helps us to organize code and development processes for large-scale software.
Subscribe to get free weekly content on data structure and algorithms, machine learning, system design, oops design and mathematics.