Java With Us
Home   |   Tutorial   |   Programs  

Java Tutorial

Introduction to Java
Hello World Program
Variables and Data types
More about data types
Displaying text using print and println
Displaying text using printf
Java Comments
Naming conventions for Identifiers
Mathematical operations in Java
Taking input from the user

Classes and Objects
Introduction to object oriented programming
The constituents of a Class
Creating objects and calling methods
Get and Set Methods
Default constructor provided by the compiler
Access Specfiers
Scope and lifetime of Variables
Call by value and Call by Reference

A few more topics
Casting
Class as a reference data type
Constants or literals
Final variables
Increment and decrement operators
Manipulating Strings
Operators
Overloading constructors and methods
Static methods and variables
The Java API
The Math class
this keyword
Wrapper classes

Control Structures
Control Statements
Repetition statements
Nested loops
Formulating algorithms
Branching Statements

Arrays
Arrays introduction
Processing arrays using 1oops
Searching and sorting arrays
Array of objects
Multi dimensional arrays
Taking input as command line arguments
Using ellipsis to accept variable number of arguments

Inheritance
Inheritance introduction
Relation between a super class and sub class
Final classes and methods
The protected access specifier
Class Object

Polymorphism
Introduction
Interfaces
Packages
Abstract classes and methods

Exception handling
Exception handling introduction
Exception hierarchy
Nested try catch blocks
Throwing exceptions


Overloading Constructors and Methods

When more than a single constructor is defined in a class, it is known as constructor overloading. Similarly, when more than one method with the same name is defined in the same class, it is known as method overloading. However, there is a restriction on such overloading. Constructors or methods can be overloaded only in a way that would allow calls to these constructors or methods to be resolved i.e. when an overloaded method is invoked, the compiler should be able to decide which of the overloaded methods is to be called by checking the arguments. For example consider an overloaded method print() having two versions. The first of these does not take any arguments while the second one takes a single argument. A statement like the following is a call to definitely the second version of the overloaded method which requires a single String argument.

print ( "example");

However, if both the versions of the overloaded methods take a single String argument, then the call is non resolvable. A statement like the above could be a call to either the first or to the second version of the method. Therefore, the overloaded methods here would give compilation errors. In short, it can be stated that overloading of methods or constructors can be done only if the parameter list varies in atleast one of the following things: number of arguments, types of arguments or order of arguments. The return types of the overloaded method doesn't matter.

Methods calls to overloaded versions are resolved by looking at the above stated three parameters. Following statements shows a few sets of overloaded methods and also specify if that particular set is allowed in a single class. The reason is also stated in each case.

Set 1:

public void print()
public void print ( String str)

This set is allowed since the number of arguments differ in the two cases.

Set 2:

public void print ( int a)
public void print ( String s)

This set is also allowed since the type of parameters are different even though the number of arguments are the same. One of the overloaded versions of print accepts an integer argument while the other accepts a String argument.

Set 3:

public void print ( int x )
public void print ( int y)

This set may appear as acceptable at the first look but it is not. These versions do not differ in either the number, order or type of arguments. The variable name in the parameter list doesn't really matter. For example, consider the following call:

print(34);

This might be a call to either the first method or the second method. Hence this set is not allowed. The ultimate rule to check the validity of overloaded methods is to see if a method call is resolvable i.e. there should be a one to one correspondence between a method call and a method. A method call should always point to a single method and there should be no doubt as to which of the methods would be called.

Set 4:

public void print ( int a, String s)
public void print ( String s, int a )

This set is an acceptable set for overloading the methods since the parameters differ in the order in which they are specified even though the number and type of parameters is the same.

Set 5:

public void print ( int a)
public int print ( int a)

The above sets of methods cannot be declared in the same class. As already said, the return type doesn't matter when deciding the mutual co-existence of overloaded methods. One might argue that the call can be resolved depending on whether the call requires a value to be returned. But such an argument isn't valid because, it isn't necessary that a returned value should always be used in one or the other way. Consider the following method call.

print(34);

Looking at this call, one can't say that the print() method doesn't return a value. If we had only the second version of print (the one that returns an int) out of the two stated versions above, that version would be the one to be called. The returned int would be simply ignored. In short, values returned by a method need not always be put to use and therefore the return type isn't checked to verify the validity of overloaded methods. In a similar way, we can check the extent to which we can overload constructors for a class.

The following class contains overloaded versions of the method findVolume(): the first overloaded version calculates the volume of a cube, the second one calculates the volume of a cylinder and the third calculates the volume of a cuboid.

class Volume {

   public void findVolume ( int s) {
      System.out.println ( "Volume of cube is "+ ( s * s * s ) );
   }

   public void findVolume ( int r, int h ) {
      System.out.println ( "Volume of cylinder is "+ ( 3.14 * r * r * h) );
   }

   public void findVolume ( int l, int b, int h) {
      System.out.println ("Volume of cuboid is " + ( l * b * h ) );
   }
}

And here is a test class

class VolumeTest {

   public static void main(String[] args) {
      Volume v=new Volume();
      v.findVolume(3);
      v.findVolume(3,4);
      v.findVolume(3,4,7);
   }
}

The output is

Volume of cube is 27
Volume of cylinder is 113.03999999999999
Volume of cuboid is 84

One particular thing is to be noted here. We have always called the method findVolume() not bothering if the call would be diverted to the method intended for the cube, cylinder or cuboid. If Java hadn't supported method overloading, there would have been a need to write different names for each method. Remembering all those names and calling the correct version might not always be convenient. Method overloading is one of the ways by which Java implements polymorphism which is the ability to pass the same message and still receive the correct response. In this particular example, the message passed was the same i.e. findVolume() and the result was the required one in all the three cases.

As already said, when resolving method calls to overloaded methods, the data types of arguments passed are verified against the data types of the parameters specified in the parameter list of the methods. This match between the arguments and the parameters need not be always exact. Automatic type casting takes place if no exact match is found. Casting always occurs to higher data types. For example, a byte may be cast into an int but an int is never cast into an int. The former is known as up casting ( casting to higher data types) while the latter is known as down casting ( casting to lower data types). For instance consider the following statements:

byte b = 3;
v.findVolume(b);

This particular method call doesn't match with any of the overloaded versions but still the first version would be called. This is because of automatic type casting. The byte is first cast to a short and checked if the method call is resolvable. As no match occurs even after casting to a short, the short is further casted to an int. Now, a match occurs with the first version of the method and hence the call gets diverted to it.

If in the above example, we had two versions of findVolume() method, which accept a single parameter as shown below; one of them accepting short and the other an int; the method call above would call the version that accepts a short. This is because casting takes place in gradual steps to higher levels. Once a match is found, casting to higher data types is halted.

findVolume ( short a) // version 1
findVolume ( int a) // version 2
byte a=4;
v.findVolume(a); // version 1 is called and not version 2

There is another rule which determines the method to be called during automatic type casting: when the method call contains more than one parameter, after casting either of the arguments, the method calls should match with only on of the overloaded versions. Otherwise compilation fails. For example, consider that we have the following two versions of a method print:

public void print ( int a, byte b)
public void print ( byte a, int b)

And we have the following method call

byte a=3,b=4;
print(a,b);

No exact match is found for the above method call and the call is not resolvable even after casting. The reason is that if a is casted to an int and b is left as a byte, then the call would be to the first version of the method print(). However, if b is cast to an int and a is left as a byte, the call would be to the second version of the method. There is no reason why either the variable a or the variable b needs to be casted to an int in preference to the other. Hence, the call is non resolvable and would generate a compilation error.

Privacy Policy