ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Learning Polymorphism and Object Serialization

by Budi Kurniawan
03/08/2001

While most textbooks teach polymorphism by drawing UML diagrams, the Brainy Draw project encourages you to draw. While others make up hypothetical objects to use as examples for object serialization, we'll discuss the real need to persist your objects. The Brainy Draw project in this article is a fun way to learn polymorphism and object serialization.

Polymorphism and object serialization are two important topics in Java. While object serialization is easy to grasp once you understand the concept of objects, polymorphism is sometimes a thorny subject. This article first introduces you with a brief theory of both topics, but won't spend too much time with theories, because there are heaps of decent Java books that explain those topics in details. This article, on the other hand, spends most of the space discussing the application in which polymorphism and object serialization is used. You can download the code and have a play with it. In this Internet and PS2 era, I still believe in the old saying "practice makes perfect."

Polymorphism

Polymorphism is one of the three fundamental principles in object oriented programming. The other two are encapsulation and inheritance. Of the three, I would say polymorphism is the hardest to comprehend. To learn polymorphism, you should understand inheritance.

Inheritance is a mechanism that allows a class to be defined as a specialization of another class. It is said that the more specialized class is a subclass or a child class of the more general class. The class where the child class is derived from is also called the parent class. Inheritance gets its name because the subclass inherits all the properties and methods from the parent class, except those that are private to the parent class. Why do we extend a class? For code reuse; the structure and behavior already implemented by one class may be used to define other classes. If someone else has already written a class that satisfies 85% of your need, you can just extend the class and save time by implementing only the remaining 15% of functionality.

Polymorphism is briefly described as "one interface, many implementations." When dealing with class hierarchies, you often want to treat an object not as the specific type that it is, but instead as its base type. This allows you to write code that doesn't depend on specific types.

This is often explained with the classic example, Shape. Consider the class hierarchy in Figure 1.

Figure 1.
Figure 1. The Shape base class is implemented by Rectangle, Oval and Line

In this hierarchy, Rectangle, Oval, and Line are more specialized forms of Shape. They share some basic behavior, though. If Shape has a method called draw, Rectangle, Oval, and Line inherit this method. However, the draw method inherited can be overridden in the child class. This method overriding is actually what makes things more interesting.

Because Rectangle, Oval and Line are derived from Shape, they are themselves Shapes. Therefore you can write something like the following code, for which the technical term is upcasting.

Shape shape = new Rectangle();

or

Shape shape = new Oval();

or

Shape shape = new Line();

It is legal to store a Rectangle object in a Shape object reference because a Rectangle is also a Shape. The other way around isn't true, though. A Shape is not always a Rectangle.

Now sometimes you have a Shape object reference containing an object of a subclass of Shape, but you don't know what exactly the object is, i.e. whether the object is a rectangle, oval, or line. However, you can call the draw method of Shape, and the correct implementation will be called. In other words, if the object happens to be Oval, the draw method in the Oval class is called. If it is a Line, then the draw method in Line is invoked.

Why this is very useful is explained better when you build the implementation in the Brainy Draw project.

Object serialization

With object serialization, you can take any Java object that implements the Serializable interface and turn it into a sequence of bytes stored in a file or sent across a network. These bytes can later be restored to regenerate the original object. This feature provides a lightweight persistence mechanism for your objects.

Object serialization also works across operating systems. This means you can run a Java application on Windows and serialize an object into a file. Later you can open this file on Linux and still obtain the same object. If the object you serialized held references to other objects, those other objects are serialized too, provided they also implement the Serializable interface.

The good thing about object serialization is Java takes care of the byte formats and ordering and all other details. This really saves a lot of coding time.

To serialize an object, you need some sort of OutputStream object that is wrapped inside an ObjectOutputStream object. Afterwards, you can use the writeObject method of the ObjectOutputStream class to serialize your object by passing the object as a parameter to the writeObject method.

To restore the object, you can reverse the process by wrapping an InputStream object inside an ObjectInputStream object. With this ObjectInputStream object, you can call its readObject method to obtain your object. What is returned by the readObject method is an Object object. You need to downcast it to the correct type to get the original object.

As mentioned above, this article does not intend to elaborate on theories. For more information, you are referred to Sun's Java site or many Java books in the bookstore. You can, however, see object serialization in action in the Brainy Draw project.

The Brainy Draw Project

The project that will highlight polymorphism and serialization in this article is called Brainy Draw. This is a vector-based graphic tool that allows you to draw different shapes of any size. An example of another vector-based graphics application is Corel Draw. In this type of drawing tool, each shape is an object that you paste on the drawing area. Each object is an independent object whose properties (thickness, color) can be changed and can be resized at any time during and after the drawing process. The other type of graphics application is a bitmap-based tool such as the Paint program in Windows. In this kind of application, each object is converted into a bitmap representation. Once you draw an object, the object blends with the other objects already on the drawing board. If you save the drawing and open it later, you won't be able to separate individual objects from each other because they are now all bitmaps.

The sole purpose of Brainy Draw's existence is to demonstrate polymorphism and object serialization in action. As such, Brainy Draw is kept simple, as a learning tool should be. Nevertheless, it satisfies the requirements of being called a graphics tool: Open New, Open, Save, and Undo features, as well as a click and drag feature to easily draw rectangles, ovals, and lines.

The three shapes demonstrate the use of polymorphism, and the Save and Open features shows object serialization at work.

Figure 3 shows the project graphical interface and Listing 1 gives the complete project code.

Figure 3
Figure 3. The Brainy Draw project.

Polymorphism: The Shape interface and three implementations

All the drawing shapes in Brainy Draw must implement the Shape interface. The Shape interface has only one method, the draw method. The signature of the draw method is given in the interface listed below.

interface Shape extends Serializable {
  public void draw(java.awt.Graphics g);
}

The draw method accepts one argument: an object reference of type java.awt.Graphics. It is intended that each class which implements Shape draws itself on this Graphics object. More information on how to obtain and use a Graphics object can be found in the "The Drawing Board" section.

Pages: 1, 2, 3

Next Pagearrow