Interchangeable objects with polymorphism | CodeGuru

Interchangeable objects with polymorphism

Bruce Eckel’s Thinking in Java Contents | Prev | Next with polymorphism Inheritance usually ends up creating a family of classes, all based on the same uniform interface. We express this with an inverted tree diagram: [5] One of the most important things you do with such a family of classes is to treat an […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
5 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

with
polymorphism

Inheritance


usually ends up creating a family of classes, all based on the same uniform


interface. We express this with an inverted tree diagram:


[5]

One


of the most important things you do with such a family of classes is to treat


an object of a derived class as an object of the base class. This is important


because it means you can write a single piece of code that ignores the specific


details of type and talks just to the base class. That code is then


decoupled

from type-specific information, and thus is simpler to write and easier to


understand. And, if a new type – a


Triangle

,


for example –



is


added through inheritance, the code you write will work just as well for the


new type of


Shape

as it did on the existing types. Thus the program is


extensible

.

Consider


the above example. If you write a function in Java:

void doStuff(Shape s) {
  s.erase();
  // ...
  s.draw();
}

This


function speaks to any


Shape

,


so it is independent of the specific type of object it’s drawing and


erasing. If in some other program we use the


doStuff( )

function:

Circle c = new Circle();
Triangle t = new Triangle();
Line l = new Line();
doStuff(c);
doStuff(t);
doStuff(l);

The


calls to


doStuff( )

automatically


work right, regardless of the exact type of the object.

This


is actually a pretty amazing trick. Consider the line:

doStuff(c);

What’s


happening here is that a


Circle

handle is being passed into a function that’s expecting a


Shape

handle. Since a


Circle
is

a


Shape

it can be treated as one by


doStuff( )

.


That is, any message that


doStuff( )

can send to a


Shape

,


a


Circle

can accept. So it is a completely safe and logical thing to do.

We


call this process of treating a derived type as though it were its base type


upcasting

.


The name


cast

is


used in the sense of casting into a mold and the


up

comes from the way the inheritance diagram is typically arranged, with the base


type at the top and the derived classes fanning out downward. Thus, casting to


a base type is moving up the inheritance diagram: upcasting.

An


object-oriented program contains some upcasting somewhere, because that’s


how you decouple yourself from knowing about the exact type you’re


working with. Look at the code in


doStuff( )

:

  s.erase();
  // ...
  s.draw();

Notice


that it doesn’t say “If you’re a


Circle

,


do this, if you’re a


Square

,


do that, etc.” If you write that kind of code, which checks for all the


possible types a


Shape

can actually be, it’s messy and you need to change it every time you add


a new kind of


Shape

.


Here, you just say “You’re a shape, I know you can


erase( )

yourself,


do it and take care of the details correctly.”


Dynamic
binding

What’s


amazing about the code in


doStuff( )

is that somehow the right thing happens. Calling


draw( )

for


Circle

causes different code to be executed than when calling


draw( )

for


a


Square

or a


Line

,


but when the


draw( )

message is sent to an anonymous


Shape

,


the correct behavior occurs based on the actual type that the


Shape

handle happens to be connected to. This is amazing because when the Java


compiler is compiling the code for


doStuff( )

,


it cannot know exactly what types it is dealing with. So ordinarily,


you’d expect it to end up calling the version of


erase( )

for


Shape

,


and


draw( )

for


Shape

and not for the specific


Circle

,


Square

,


or


Line

.


And yet the right thing happens. Here’s how it works.

When


you send a message to an object even though you don’t know what specific


type it is, and the right thing happens, that’s called


polymorphism

.


The process used by object-oriented programming languages to implement


polymorphism is called


dynamic
binding

.


The compiler and run-time system handle the details; all you need to know is


that it happens and more importantly how to design with it.

Some


languages require you to use a special keyword to enable dynamic binding. In


C++ this keyword is


virtual

.


In Java, you never need to remember to add a keyword because functions are


automatically dynamically bound. So you can always expect that when you send a


message to an object, the object will do the right thing, even when upcasting


is involved.


Abstract
base classes and interfaces

Often


in a design, you want the base class to present


only

an interface for its derived classes. That is, you don’t want anyone to


actually create an object of the base class, only to upcast to it so that its


interface can be used. This is accomplished by making that class


abstract

using


the


abstract

keyword. If anyone tries to make an object of an


abstract

class, the compiler prevents them. This is a tool to enforce a particular design.

You


can also use the


abstract

keyword to describe a method that hasn’t been implemented yet – as


a stub indicating “here is an interface function for all types inherited


from this class, but at this point I don’t have any implementation for


it.” An


abstract

method


may be created only inside an


abstract

class.


When the class is inherited, that method must be implemented, or the inherited


class becomes


abstract

as well. Creating an


abstract

method allows you to put a method in an interface without being forced to


provide a possibly meaningless body of code for that method.

The


interface

keyword takes the concept of an


abstract

class one step further by preventing any function definitions at all. The


interface

is a very useful and commonly-used tool, as it provides the perfect separation


of interface and implementation. In addition, you can combine many interfaces


together, if you wish. (You cannot inherit from more than one regular


class

or


abstract
class

.)


[5]

This uses the


Unified
Notation

,


which will primarily be used in this book.

Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.