JavaEnterprise JavaThe Essence of OOP Using Java, Polymorphism Based on Overloaded Methods

The Essence of OOP Using Java, Polymorphism Based on Overloaded Methods

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java Programming Notes #1608


Preface

This lesson is one of a series of lessons designed to teach you about the
essence of Object-Oriented Programming (OOP) using Java.

The first lesson in the group was entitled
The
Essence of OOP Using Java, Objects, and Encapsulation
.  That lesson,
and each of the lessons following that one, has provided explanations of
certain aspects of the essence of Object-Oriented Programming using Java. 
The previous lesson was entitled
The Essence of
OOP using Java, Inheritance, Part 2
.

Necessary and significant aspects

This miniseries will describe and discuss the necessary and
significant
aspects of OOP using Java.

If you have a general understanding of computer programming, you should
be able to read and understand the lessons in this miniseries, even if
you don’t have a strong background in the Java programming language.

Viewing tip

You may find it useful to open another copy of this lesson in a separate
browser window.  That will make it easier for you to scroll back and
forth among the different listings while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection
of online Java tutorials.  You will find those lessons published at
Gamelan.com
However, as of the date of this writing, Gamelan doesn’t maintain a consolidated
index of my Java tutorial lessons, and sometimes they are difficult to
locate there.  You will find a consolidated index at
Baldwin’s
Java Programming Tutorials
.

Preview


Previous lessons introduced overloading and overriding methods. 
This lesson concentrates on the use of method overloading to achieve compile-time
polymorphism
.

Every class in Java is a direct or indirect subclass of the class named
Object
Methods defined in the class named Object are inherited into all
other classes.  They may be overridden to make their behavior more
appropriate to objects instantiated from the new class.

Overloaded methods have the same name and different formal argument
lists.  They may or may not have the same return type.

Polymorphism manifests itself in Java in the form of multiple methods
having the same name.  This lesson concentrates on method overloading,
sometimes referred to as compile-time polymorphism.  Subsequent
lessons concentrate on method overriding, sometimes referred to as runtime
polymorphism.

Overloaded methods may all be defined in the same class, or may be defined
in different classes as long as those classes have a superclass-subclass
relationship.

Discussion
and Sample Code


Three concepts

In an earlier lesson, I explained that most books on OOP will tell you
that in order to understand OOP, you must understand the following three
concepts:

  • Encapsulation
  • Inheritance
  • Polymorphism

I agree with that assessment.

Encapsulation and inheritance

Previous lessons in this series have explained Encapsulation and Inheritance. 
This lesson will tackle the somewhat more complex topic of Polymorphism.

Overloading and overriding methods

In the lessons on inheritance, you learned a little about overloading
and overriding methods (you will learn more about these concepts
as you progress through these lessons)
.  This lesson concentrates
on the use of overloaded methods to achieve compile-time polymorphism.

Real-world scenarios

The sample programs that I used in the previous lessons in this series
dealt with two kinds of car radios:

  • Plain car radios
  • Car radios having built-in tape players

I couched those programs in a real-world scenario in an attempt to convince
you that encapsulation and inheritance really do have a place in the real
world.

Programs were fairly long

However, even though those programs were simple in concept, they were
relatively long.  This made them somewhat difficult to explain due
simply to the amount of code involved.

Keep it short and simple

At this point, I am going to back away from real-world scenarios and
begin using sample programs that are as short and as simple as I know how
to make them, while still illustrating the important points regarding polymorphism. 
My objective is make the polymorphic concepts as clear as possible without
having those concepts clouded by other programming issues.

I will simply ask you to trust me when I tell you that polymorphism
has enormous application in the real world.

A little more on inheritance

There is another aspect of inheritance that I didn’t explain in the
previous lessons in this series.

Every class extends some other class

Every class in Java extends some other class.  If you don’t explicitly
specify the class that your new class extends, it will automatically extend
the class named Object.

A class hierarchy

Thus, all classes in Java exist in a class hierarchy where the class
named Object forms the root of the hierarchy.

Some classes extend Object directly, while other classes are
subclasses of Object further down the hierarchy.

Methods in the Object class

The class named Object defines default versions of the following
methods:

  • clone()
  • equals(Object obj)
  • finalize()
  • getClass()
  • hashCode()
  • notify()
  • notifyAll()
  • toString()
  • wait()
  • wait(long timeout)
  • wait(long timeout, int nanos)

As you can see, this list includes three overloaded versions of the method
named wait (same name, different formal argument lists).

Every class inherits these methods

Because every class is either a direct or indirect subclass of Object,
every class in Java, (including new classes that you define), inherit
these eleven methods.

To be overridden …

Generally speaking, many of these eleven methods are intended to be
overridden for various purposes.  However, some of them, such as getClass,
notify,
and wait, are intended to be used directly without overriding.

What is polymorphism?

The meaning of the word polymorphism is something like one name,
many forms
.

How does Java implement polymorphism?

Polymorphism manifests itself in Java in the form of multiple methods
having the same name.

In some cases, multiple methods have the same name, but different formal
argument lists (overloaded methods).

In other cases, multiple methods have the same name, same return type,
and same formal argument list (overridden methods).

Three distinct forms of polymorphism

From a practical programming viewpoint, polymorphism manifests itself
in three distinct forms in Java:

  • Method overloading
  • Method overriding through inheritance
  • Method overriding through the Java interface

Method overloading

I will begin the discussion of polymorphism with method overloading,
which is the simpler of the three.  I will cover method overloading
in this lesson and will cover polymorphism based on overridden methods
and interfaces in subsequent lessons.

Method overloading versus method overriding

Don’t confuse method overloading with method overriding.

Java allows you to have two or more method definitions in the same scope
with the same name, provided that they have different formal argument lists.

More specifically, here is what Roberts, Heller, and Ernest have to
say about overloading methods in their excellent book entitled The Complete
Java 2 Certification Study Guide
:

“A valid overload differs in the number or type of its arguments. 
Differences in argument names are not significant.  A different return
type is permitted, but is not sufficient by itself to distinguish an overloading
method.”

Similarly, here is what they have to say about method
overriding:

“A valid override has identical argument types and order,
identical return type, and is not less accessible than the original method. 
The overriding method must not throw any checked exceptions that were not
declared for the original method.”

You should read these two descriptions carefully
and make certain that you recognize the differences.

Compile-time polymorphism

Some authors refer to method overloading as a form of compile-time
polymorphism,
as distinguished from run-time polymorphism.

This distinction comes from the fact that, for each method invocation,
the compiler determines which method (from a group of overloaded methods)
will be executed, and this decision is made when the program is compiled.
(In
contrast, I will tell you later that the determination of which overridden
method to execute isn’t made until runtime.)

Selection based on the argument list

In practice, the compiler simply examines the types, number, and order
of the parameters being passed in a method invocation, and selects the
overloaded method having a matching formal argument list.

A sample program

I will discuss a sample program named Poly01 to illustrate method
overloading.  A complete listing of the program can be viewed in Listing
4 near the end of the lesson.

Within the class and the hierarchy

Method overloading can occur both within a class definition, and vertically
within the class inheritance hierarchy.  (In other words, an overloaded
method can be inherited into a class that defines other overloaded versions
of the method.) 
The program named Poly01 illustrates both
aspects of method overloading.

Class B extends class A, which extends Object

Upon examination of the program, you will see that the class named
A
extends the class named Object.  You will also see that the
class named B extends the class named A.

The class named Poly01 is a driver class whose main method
exercises the methods defined in the classes named A and B.

Once again, this program is not intended to correspond to any particular
real-world scenario.  Rather, it is a very simple program designed
specifically to illustrate method overloading.

Will discuss in fragments

As is my usual approach, I will discuss this program in fragments.

The code in Listing 1 defines the class named A, which explicitly
extends Object.

 

class A extends Object{
  public void m(){
    System.out.println("m()");
  }//end method m()
}//end class A

Listing 1

Redundant code

Recall that explicitly extending Object is not required
(but
it also doesn’t hurt anything)
.

By default, the class named A would extend the class named Object
automatically, unless the class named A explicitly extends some
other class.

The method named m()

The code in Listing 1 defines a method named m().  Note
that this version of the method has an empty argument list (it doesn’t
receive any parameters when it is executed)
.  The behavior of
the method is simply to display a message indicating that it has been invoked.

The class named B

Listing 2 contains the definition for the class named B
The class named B extends the class named A, and inherits
the method named m defined in the class named A.

 

class B extends A{
  public void m(int x){
    System.out.println("m(int x)");
  }//end method m(int x)
  //---------------------------------//
  
  public void m(String y){
    System.out.println("m(String y)");
  }//end method m(String y)
}//end class B

Listing 2

Overloaded methods

In addition to the inherited method named m, the class named
B
defines two overloaded versions of the method named m:

  • m(int x)
  • m(String y)

(Note that each of these two versions of the method receives a single
parameter, and the type of the parameter is different in each case.)

As with the version of the method having the same name defined in the
class named A, the behavior of each of these two methods is simply
to display a message indicating that it has been invoked.

The driver class

Listing 3 contains the definition of the driver class named Poly01.

 

public class Poly01{
  public static void main(
                        String[] args){
    B var = new B();
    var.m();
    var.m(3);
    var.m("String");
  }//end main
}//end class Poly01

Listing 3

Invoke all three overloaded methods

The code in the main method

  • Instantiates a new object of the class named B
  • Successively invokes each of the three overloaded versions of the method
    named m on the reference to that object.

One version is inherited

Note that the overloaded version of the method named m, defined
in the class named A, is inherited into the class named B
Hence, it can be invoked on a reference to an object instantiated from
the class named B.

Two versions defined in class B

The other two versions of the method named m are defined in the
class named B.  Thus, they also can be invoked on a reference
to an object instantiated from the class named B.

The output

As you would expect, the output produced by sending messages to the
object asking it to execute each of the three overloaded versions of the
method named m is:

m()

m(int x)

m(String y)

Note that the values of the parameters passed to the methods do not
appear in the output.  Rather, the parameters are used solely to make
it possible for the compiler to select the correct version of the overloaded
method to execute in each case.

This output confirms that each overloaded version of the method is properly
selected for execution based on the matching of method parameters to the
formal argument list of each method.

Summary


Previous lessons introduced overloading and overriding methods.  This
lesson concentrates on the use of method overloading to achieve compile-time
polymorphism.

All classes in Java form a hierarchy with a class named Object
at the root of the hierarchy.  Thus, every class in Java is a direct
or indirect subclass of the class named Object.

If a new class doesn’t explicitly extend some other class, it will,
by default, automatically extend the class named Object.

The Object class defines default versions of eleven different
methods.  These methods are inherited into all other classes, and
may be overridden to make their behavior more appropriate to objects instantiated
from the new class.

Overloaded methods have the same name and different formal argument
lists.  They may or may not have the same return type.

Three of the eleven methods defined in the class named Object
are overloaded.  The names of the three overloaded methods are wait
One version takes no parameters.  A second version takes a single
parameter of type long.  The third version takes two parameters,
one of type long, and one of type int.

The word polymorphism means something like one name, many forms. 
Polymorphism manifests itself in Java in the form of multiple methods having
the same name.

Polymorphism manifests itself in three distinct forms in Java:

  • Method overloading
  • Method overriding through inheritance
  • Method overriding through the Java interface

This lesson concentrates on method overloading, sometimes referred to as
compile-time
polymorphism. 
This form of polymorphism is distinguished by the
fact that the compiler selects among a group of overloaded methods on the
basis of the types and the number of parameters passed to the method when
it is invoked.  The selection is made when the program is compiled
(rather
than being made later when the program is run)
.

Overloaded methods may all be defined in the same class, or may be defined
in different classes as long as those classes have a superclass-subclass
relationship in the class hierarchy.

The sample program in this lesson illustrates three overloaded versions
of the same method name with two of the versions being defined in a single
class, and the other version being defined in the superclass of that class.

What’s Next?


The next lesson teaches you about assignment compatibility, type conversion,
and casting for both primitive and reference types.  It also teaches
you about the relationship between reference types, method invocations,
and the location in the class hierarchy where a method is defined.

Complete Program Listing


A complete listing of the program is shown in Listing 4 below.

 

/*File Poly01.java
Copyright 2002, R.G.Baldwin

Program output is:
m()
m(int x)
m(String y)
**************************************/

class A extends Object{
  public void m(){
    System.out.println("m()");
  }//end method m()
}//end class A
//===================================//

class B extends A{
  public void m(int x){
    System.out.println("m(int x)");
  }//end method m(int x)
  //---------------------------------//
  
  public void m(String y){
    System.out.println("m(String y)");
  }//end method m(String y)
}//end class B
//===================================//

public class Poly01{
  public static void main(
                        String[] args){
    B var = new B();
    var.m();
    var.m(3);
    var.m("String");
  }//end main
}//end class Poly01
//===================================//

Listing 4

About the author

Richard Baldwin
is a college professor (at Austin Community College in Austin, TX) and
private consultant whose primary focus is a combination of Java and XML.
In addition to the many platform-independent benefits of Java applications,
he believes that a combination of Java and XML will become the primary
driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving
Java, XML, or a combination of the two.  He frequently provides onsite
Java and/or XML training at the high-tech companies located in and around
Austin, Texas.  He is the author of Baldwin’s Java Programming Tutorials,
which has gained a worldwide following among experienced and aspiring Java
programmers. He has also published articles on Java Programming in Java
Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and
has many years of experience in the application of computer technology
to real-world problems.

baldwin.richard@iname.com

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories