Main Page

Previous Next

Method Overloading

Java allows you to define several methods in a class with the same name, as long as each method has a set of parameters that is unique. This is called method overloading.

The name of a method together with the type and sequence of the parameters form the signature of the method - the signature of each method in a class must be distinct to allow the compiler to determine exactly which method you are calling at any particular point.

Note that the return type has no effect on the signature of a method. You cannot differentiate between two methods just by the return type. This is because the return type is not necessarily apparent when you call a method. For example, suppose you write a statement such as:

Math.round(value);

Although the statement above is pointless since we discard the value that the round() method produces, it does illustrate why the return type cannot be part of the signature for a method. There is no way for the compiler to know from this statement what the return type of the method round() is supposed to be. Thus, if there were several different versions of the method round(), and the return type was the only distinguishing aspect of the method signature, the compiler would be unable to determine which version of round() you wanted to use.

There are many circumstances where it is convenient to use method overloading. You have already seen that the standard class Math contains two versions of the method round(), one that accepts an argument of type float, and the other that accepts an argument of type double. You can see now that method overloading makes this possible. It would be rather tedious to have to use a different name for each version of round() when they both do essentially the same thing. The valueOf() method in the String class is another example. There is a version of this method for each of the basic types. One context in which you will regularly need to use overloading is when you write constructors for your classes, which we'll look at now.

Multiple Constructors

Constructors are methods that can be overloaded, just like any other method in a class. In most situations, you will need to generate objects of a class from different sets of initial defining data. If we just consider our class Sphere, we could conceive of a need to define a Sphere object in a variety of ways. You might well want a constructor that accepted just the (x, y, z) coordinates of a point, and have a Sphere object created with a default radius of 1.0. Another possibility is that you may want to create a default Sphere with a radius of 1.0 positioned at the origin, so no arguments would be specified at all. This requires two constructors in addition to the one we have already written.

Try It Out - Multiple Constructors for the Sphere Class

The code for the extra constructors is:

class Sphere {
  // First Constructor and variable declarations
  ...
  // Construct a unit sphere at a point
  Sphere(double x, double y, double z) {
    xCenter = x;
    yCenter = y;
    zCenter = z;
    radius = 1.0;
    ++count;                    // Update object count
  }

  // Construct a unit sphere at the origin
  Sphere() {
    xCenter = 0.0;
    yCenter = 0.0;
    zCenter = 0.0;
    radius = 1.0;
    ++count;                    // Update object count
  }

  // The rest of the class as before...
}

The statements in the default constructor that set three data members to zero are not really necessary, as the data members would be set to zero by default. They are there just to emphasize that the primary purpose of a constructor is to enable you to set initial values for the data members.

If you add the following statements to the CreateSpheres class, you can test out the new constructors:

public class CreateSpheres {
  public static void main(String[] args) {
    System.out.println("Number of objects = " + Sphere.getCount());

    Sphere ball = new Sphere(4.0, 0.0, 0.0, 0.0);               // Create a sphere
    System.out.println("Number of objects = " + ball.getCount());

    Sphere globe = new Sphere(12.0, 1.0, 1.0, 1.0);             // Create a sphere
    System.out.println("Number of objects = " + Sphere.getCount());

    Sphere eightBall = new Sphere(10.0, 10.0, 0.0);
    Sphere oddBall = new Sphere();
    System.out.println("Number of objects = " + Sphere.getCount());

    // Output the volume of each sphere
    System.out.println("ball volume = " + ball.volume());
    System.out.println("globe volume = " + globe.volume());
    System.out.println("eightBall volume = " + eightBall.volume());
    System.out.println("oddBall volume = " + oddBall.volume());
  }
}

Now the program should produce the output:

Number of objects = 0
Number of objects = 1
Number of objects = 2
Number of objects = 4
ball volume = 267.94666666666666
globe volume = 7234.559999999999
eightBall volume = 4.1866666666666665
oddBall volume = 4.1866666666666665

How It Works

When you create a Sphere object, the compiler will select the constructor to use based on the types of the arguments you have specified. So, the first of the new constructors is applied in the first statement that we added to main(), as its signature fits with the argument types used. The second statement that we added clearly selects the last constructor as no arguments are specified. The other additional statements are there just to generate some output corresponding to the new objects. You can see from the volumes of eightBall and oddBall that they both are of radius 1.

It is the number and types of the parameters that affect the signature of a method, not the parameter names. If you wanted a constructor that defined a Sphere object at a point, by specifying the diameter rather than the radius, you have a problem. You might try to write it as:

  // Illegal constructor!!!
  // This WON'T WORK because it has the same signature as the original!!!
  Sphere(double diameter, double x, double y, double z) {
    xCenter = x;
    yCenter = y;
    zCenter = z;
    radius = diameter/2.0;
  }

If you try adding this to the Sphere class and recompile, you'll get a compile-time error. This constructor has four arguments of type double, so its signature is identical to the first constructor that we wrote for the class. This is not permitted - hence the compile-time error. When the number of parameters is the same in two overloaded methods, at least one pair of corresponding parameters must be of different types.

Calling a Constructor from a Constructor

One class constructor can call another constructor in the same class in its first executable statement. This can often save duplicating a lot of code. To refer to another constructor in the same class, you use this as the name, followed by the appropriate arguments between parentheses. In our Sphere class, we could have defined the constructors as:

class Sphere {
  // Construct a unit sphere at the origin
  Sphere() {
    radius = 1.0;
    // Other data members will be zero by default
    ++count;                    // Update object count
  }

// Construct a unit sphere at a point
  Sphere(double x, double y, double z)
  {
    this();                    // Call the constructor with no arguments
    xCenter = x;
    yCenter = y;
    zCenter = z;
  }

  Sphere(double theRadius, double x, double y, double z) {
    this(x, y, z);              // Call the 3 argument constructor
    radius = theRadius;         // Set the radius
  }
  // The rest of the class as before...
}

In the constructor that accepts the point coordinates as argument, we call the default constructor to set the radius and increment the count of the number of objects. In the constructor that sets the radius, as well as the coordinates, the constructor with three arguments is called to set the coordinates, which in turn will call the constructor that requires no arguments.

Duplicating Objects Using a Constructor

When we were looking at how objects were passed to a method, we came up with a requirement for duplicating an object. The need to produce an identical copy of an object occurs surprisingly often.

Important 

Java provides a clone() method, but the details of using it must wait for the next chapter.

Suppose you declare a Sphere object with the following statement:

Sphere eightBall = new Sphere(10.0, 10.0, 0.0); 

Later in your program you want to create a new object newBall, which is identical to the object eightBall. If you write:

Sphere newBall = eightBall;

this will compile OK but it won't do what you want. You will remember from our earlier discussion that the variable newBall will reference the same object as eightBall. You will not have a distinct object. The variable newBall, of type Sphere, is created but no constructor is called, so no new object is created.

Of course, you could create newBall by specifying the same arguments to the constructor as you used to create eightBall. In general, however, it may be that eightBall has been modified in some way during execution of the program, so you don't know that its instance variables have the same values - for example, the position might have changed. This presumes that we have some other class methods that alter the instance variables. You could fix this by adding a constructor to the class that will accept an existing Sphere object as an argument:

// Create a sphere from an existing object
Sphere(final Sphere oldSphere) {
  radius = oldSphere.radius;
  xCenter = oldSphere.xCenter;
  yCenter = oldSphere.yCenter;
  zCenter = oldSphere.yCenter;
}

This works by copying the values of the instance variables of the Sphere object, passed as an argument, to the corresponding instance variables of the new object.

Now you can create newBall as a distinct object by writing:

Sphere newBall = new Sphere(eightBall);  // Create a copy of eightBall

Let's recap what we have learned about methods and constructors with another example.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor