Method Overloading


Content

What is method overloading ?

While defining methods of a class, Java allows the use of a same method name with different argument lists for multiple methods. This technique is called method overloading.

Example 1: method overloading

class MyAdder {

    int add (int a, int b) {
        return a + b;
    }


    float add (float a, float b) {
        return a + b;
    }

    float add (int a, float b) {
        return a + b;
    }


    float add (float a, int b) {
        return a + b;
    }


    public static void main ( String[] args ) {
       System.out.println(   add (1,   2)    ); 
       System.out.println(   add (1.5, 2.5)  ); 
       System.out.println(   add (1,   3.1)  ); 
       System.out.println(   add (1.3, 4)    ); 
    }

}
We say method add is overloaded.

Why method overloading ?

Method overloading is useful when you need to perform similar operations on different types of data. (But that's not all)

With an overloaded method, you only need to use a single method name and provide it with some parameters at the time of method invocation.

The compiler will associate each invocation with the appropriate method definition by matching the number of parameter, the types of the parameter and/or the order of the types in an invocation against the method definitions.

Example 2: without method overloading

class MyAdder {

    int intIntAdd (int a, int b) {
        return a + b;
    }


    float floatFloatAdd (float a, float b) {
        return a + b;
    }

    float intFloatAdd (int a, float b) {
        return a + b;
    }


    float floatIntAdd (float a, int b) {
        return a + b;
    }


    public static void main ( String[] args ) {
       System.out.println(   intIntAdd     (1,   2)    ); 
       System.out.println(   floatFloatAdd (1.5, 2.5)  ); 
       System.out.println(   intFloatAdd   (1,   3.1)  ); 
       System.out.println(   floatIntAdd   (1.3, 4)    ); 
    }

}
Without method overloading, the programers themselves need to specify which method to invoke.

Associating method invocations and definitions

In general, for a given method invocation, the compiler determines which method definition to associate by looking at the following information of each method definition:

These information of a method definition is called the signature of the method definition.

The association is established by checking an invocation against each complete method signature.

The matched method definition is executed.

What if there is no exact match ? Compiler tries more by implicit type casting (see later)

How to overload a method ?

Make sure that the compiler can unambiguously associate each invocation to a specific method definition.

For overloaded methods, the method names are all the same. We need to make sure that all the overloaded methods (with a same name) differ at least by one of the last three items of the signatures.

Note: return type is not part of a signature.

Example 3: good method overloading


    int myMethod (int a, int b) {
        ......
    }


    float myMethod (int a, float b) {
        ......
    }


    float myMethod (float a, int b) {
        ......
    }

    double myMethod (int a) {
        ......
    }

    double myMethod (int a, int b, int c) {
        ......
    }

Example 4: bad method overloading


    int myMethod (int a, int b) {
        ......
    }

    int myMethod (int x, int y) {
        ......
    }

    float myMethod (int a, float b) {
        ......
    }

    double myMethod (int a, float b) {
        ......
    }



Overloading constructors

Constructors are primary candicate for overloading.

By providing multiple versions of a constructor, we provide several ways to set up an object.

Example 5: constructor overloading


class Customer {

    float limit;

    float balance;


    Customer (float a) {
        limit = a;
        balance = 0;
    }

    Customer (float a, float b) {
        limit = a;
        balance = b;
    }

    void status () {
        System.out.println("limit is "+limit);
        System.out.println("balance is "+balance);
    }

    public static void main ( String [] args )  {

        Customer c1 = new Customer( (float) 1000            );
        Customer c2 = new Customer( (float) 2000, (float)500);

        c1.status ();
        c2.status ();

    }

}

What is a default constructor ? It is a constructor with an empty argument list. If you do not define such a constructor, the compiler will provide one, which does nothing (except initialize the primitive data members as their default values).

Another example

Example 6: The Watch class

class Watch { 

     int hour; 
     int minute; 
     int second; 

     void setTime(int h) { 
         setTime(h, 0, 0); 
     } 

     void setTime(int h, int m) { 
         setTime(h, m, 0); 
     } 

     void setTime(int h, int m, int s) { 
         hour = h; 
         minute = m; 
         second = s; 
     } 

     void display() {
         System.out.println(hour+":"+minute+":"+second);
     }

     public static void main( String[] args ) { 
         Watch w = new Watch(); 
         w.setTime(6,20,30); 
         w.display();   
         w.setTime(7); 
         w.display();
     } 

}

Question : How about the println() method ?

Type castings in overloading

  • In order to match a method signature, you may need explicit type casting.
  • If no exact signature matching is found, the compiler will do more before reporting an error. It casts a parameter to the bigger types and try to find a matching.
  • You would better not to rely on this implicit type casting.

    Example 7: Type casting

    
    class OverloadingTest {
    
         void prt( String s ) { System.out.println(s); }
    
         // method f1 overloaded with char, int, float and double types
    
         void f1(char x) { prt("f1(char)"); }
    
         void f1(int x) { prt("f1(int)"); }
    
         void f1(float x) { prt("f1(float)"); } 
    
         void f1(double x) { prt("f1(double)"); } 
    
         
         // method f2 overloaded with float and double types
    
         void f2(float x) { prt("f2(float)"); } 
    
         void f2(double x) { prt("f2(double)"); } 
    
    
         // method f3 not overloaded 
         void f3(double x) { prt("f3(double)"); } 
    
    
         void test1() {
            int x = 1;
            prt("int parameter:"); // invoke method f1(int)
            f1(x);
    
            float y = 2;
            prt("float parameter:");  // invoke method f1(float) 
            f1(y);
    
            double z = 2;
            prt("double parameter:"); // invoke method f1(double) 
            f1(z);
    
            prt("int parameter (casted):");  // invoke method f1(int)
            f1( (int) z );
    
         }
    
    
         void test2() {
            char c = 'A';
            prt("char parameter:"); 
            //no exact signature match. Cast c to float and invoke method f2(float) 
            f2(c);
    
            int x = 1;
            prt("int parameter:"); 
            //no exact signature match. Cast x to float and invoke method f2(float) 
            f2(x);
    
            prt("unknown type parameter:"); // invoke method f2(double) 
            f2(4.5);
         }
    
         void test3() {
    
            char c = 'A';
            prt("char parameter:"); 
            //no exact signature match. Cast c to double and invoke method f3(double) 
            f3(c);
    
            int x = 1;
            prt("int parameter:"); 
            //no exact signature match. Cast x to double and invoke method f3(double) 
            f3(x);
    
            prt("unknown type parameter:"); 
            //invoke method f3(double) 
            f3(4.5);
         }
    
    
         public static void main ( String [] args ) {
    
            OverloadingTest o = new OverloadingTest(); 
    
            o.test1();
            o.test2();
            o.test3();
       
         }
    
    }
    
    What is the output of this program ? ? ?

    int parameter:
    f1(int)
    float parameter:
    f1(float)
    double parameter:
    f1(double)
    int parameter (casted):
    f1(int)
    char parameter:
    f2(float)
    int parameter:
    f2(float)
    unknown type parameter:
    f2(double)
    char parameter:
    f3(double)
    int parameter:
    f3(double)
    unknown type parameter:
    f3(double)