Java: Overloading Versus Overriding

I have occasionally heard the terms method overloading and method overriding used interchangeably. While the difference between these two concepts can be relatively easily explained, the difference in the runtime resolution of these is more tricky for those new to Java.

In the code example shown next, both overloading and overriding are demonstrated. The overridden method is the toString method. The overriding in action is easy to spot with the use of @Override annotations (which are not required for overloading but are helpful in communicating to the compiler that overriding is intentional). The overloading is demonstrated by multiple print() methods accepting different classes in the Vehicle -> Automobile -> Car class hierarchy.

OverloadingExample.java

package dustin.examples;

import java.util.ArrayList;
import java.util.List;

/**
 * Class demonstrating overloading/overriding issues in Java.
 */
public class OverloadingExample
{
   /**
    * Print out a Vehicle's toString().
    *
    * @param theVehicle The Vehicle whose toString() will be executed.
    */
   public void print(final Vehicle theVehicle)
   {
      System.out.println("Vehicle: " + theVehicle);
   }

   /**
    * Print out an Automobile's toString().
    *
    * @param theAutomobile The Automobile whose toString() will be executed.
    */
   public void print(final Automobile theAutomobile)
   {
      System.out.println("Automobile: " + theAutomobile);
   }

   /**
    * Print out a Car's toString().
    *
    * @param theCar The Car whose toString() will be executed.
    */
   public void print(final Car theCar)
   {
      System.out.println("Car: " + theCar);
   }

   /**
    * Main test to be executed.
    */
   public void runTest()
   {
      System.out.println(
         "===== COMPILE-TIME RESOLUTION IS SUFFICIENT FOR OVERLOADING =====");
      print(new Vehicle());
      print(new Car());
      print(new Automobile());
      System.out.println(
         "===== COMPILE_TIME RESOLUTION NOT SUFFICIENT FOR OVERLOADING =====");
      final List<Vehicle> vehicles = new ArrayList<Vehicle>();
      vehicles.add(new Car());
      vehicles.add(new Automobile());
      vehicles.add(new Vehicle());
      for (final Vehicle vehicle : vehicles)
      {
         print(vehicle);
      }
   }

   /**
    * Main function to run the test demonstrating Java's overloading and
    * overriding.
    */
   public static void main(final String[] arguments)
   {
      final OverloadingExample me = new OverloadingExample();
      me.runTest();
   }

   /**
    * Parent class with its own toString implementation.
    */
   public static class Vehicle
   {
      @Override
      public String toString() {return "Vehicle";}
   }

   /**
    * Child class with its own toString implementation.
    */
   public static class Automobile extends Vehicle
   {
      @Override
      public String toString() {return "Automobile";}
   }

   /**
    * Grandchild class with its own toString implementation.
    */
   public static class Car extends Automobile
   {
      @Override
      public String toString() {return "Car";}
   }
}

When the code above is executed, the results appear as demonstrated in the screen snapshot.

As the results of this very simple example demonstrate, overloading and overriding are treated differently at runtime. The overriding always works as we would expect because the methods called are bound at runtime. The overloaded methods are bound at compile time, however, leading to results that might be somewhat unexpected.

Join the discussion
Be the first to comment on this article. Our Commenting Policies