Learning Polymorphism and Object Serialization
Pages: 1, 2, 3
The BrainyDraw Class
The BrainyDraw class is the main class that integrates all the classes explained so far. The following are some details on how each part works.
The Constructor
The constructor starts by calling the Frame parent class
constructor to create a Frame object.
super("Brainy Draw");
It then instantiates a menu bar with two menus (File and Edit). The
File menu has the following menu items: New, Open, Save, and Exit. The
Edit menu has one menu item: Undo. After adding
ActionListener objects to all the menu items, it creates
a CheckboxGroup containing three check boxes for the
three types of shapes. The user clicks an appropriate check box to
draw a shape. ItemListener objects are added to these check boxes.
It then continues by adding the MouseListener object to panel (PanelApplet object reference). It then adds a WindowListener to itself, so the user can clicks the X button to close the window.
Drawing a Shape
To draw a shape, the user clicks on one of the three check boxes to
select the type of the shape. Then the user clicks on the drawing
panel and drags the mouse to start drawing. The position where the
mouse is pressed becomes the first coordinate of the shape and the
position where the mouse is released becomes the second
coordinate. Two MouseListener methods are overridden: the
mousePressed(MouseEvent me) method and the
mouseReleased(MouseEvent me) method.
In the mousePressed method, you use the getX and getY methods of the MouseEvent object to get the first coordinate, which is passed to the class variables x1 and y1.
public void mousePressed(MouseEvent me) {
x1 = me.getX();
y1 = me.getY();
}
The mouseReleased method does the rest. It first takes the position where the mouse is released and assigns it as the second coordinate to the class variables x2 and y2. The rest of mouseReleased adds the shape to the Vector shapes. It knows the shape type by checking the shapeType value. For Rectangle and Oval objects, valid objects can't have two coordinates with the same abscissas (zero height) or the same ordinates (zero width). For a Line object, it is a bit relaxed. A Line will be created unless the two coordinates are the same.
public void mouseReleased(MouseEvent me) {
x2 = me.getX();
y2 = me.getY();
Shape s = null;
if (shapeType.equals("Rectangle")) {
// a Rectangle cannot have a zero width or height
if (x1!=x2 || y1!=y2)
s = new Rectangle(x1, y1, x2, y2);
}
else if (shapeType.equals("Line")) {
// a Rectangle cannot have a zero width or height
if (x1!=x2 && y1!=y2)
s = new Line(x1, y1, x2, y2);
}
else if (shapeType.equals("Oval")) {
// an Oval cannot have a zero width or height
if (x1!=x2 || y1!=y2)
s = new Oval(x1, y1, x2, y2);
}
if (s!=null) {
panel.shapes.add(s);
panel.repaint();
}
}
Upon creating a Shape, it adds it to the shapes
Vector in the panel applet. It then calls
the repaint method of the applet to refresh the drawing
area.
The Menu functions
Now that you know how to draw a shape, it is time to see what you can do with the menu items. The actionPerformed method from the ActionListener interface that BrainyDraw class implements redirects clicks on each menu item to the processing methods. The code for the actionPerformed method is given below. Note how the getActionCommand method of the ActionEvent class returns the source object that receives the action.
public void actionPerformed(ActionEvent ae) {
String command = ae.getActionCommand().toString();
if (command.equals("Exit"))
System.exit(0);
else if (command.equals("New"))
openNew();
else if (command.equals("Open"))
open();
else if (command.equals("Save"))
save();
else if (command.equals("Undo"))
undo();
}
Undo
The Undo menu item is connected to the undo private method.
The class signature and body are as follow.
private void undo() {
int shapeCount = panel.shapes.size();
if (shapeCount!=0) {
panel.shapes.removeElementAt(shapeCount-1);
panel.repaint();
}
}
The undo method simply cancels the last object drawn. This
is achieved by removing the last element in the shapes
Vector in the applet. To update the display, you need to call
the applet's repaint method.
Open New
Open New is connected to the openNew private method.
It basically starts with a new document and clears all the objects
drawn, if any. The openNew method is given in the
following snippet.
private void openNew() {
panel.shapes.removeAllElements();
panel.repaint();
}
Because all shape objects are stored in shapes Vector,
clearing them is as simple as calling the removeAllElements method of
the Vector object. As usual, the applet's repaint method is called to
refresh the drawing area.
Save
The Save menu item is connected to the save private
method. As the name indicates, this method saves all objects to a
file. It does so by serializing an object using the
writeObject method of the ObjectOutputStream
class. The writeObject method is a really effective
method because it not only serializes the object passed to it but also
all other objects referenced by that object. To be serializable, the
object passed must implement the Serializable
interface. If the object passed references other objects, the other
objects must also implement Serializable.
The signature and body of the save method are as follows.
private void save() {
try {
ObjectOutputStream out =
new ObjectOutputStream(
new FileOutputStream(filename));
out.writeObject(panel.shapes);
out.close(); // Also flushes output
}
catch(Exception e) {
e.printStackTrace();
}
}
Because all of the shape objects are stored in shapes
Vector, you need only serialize the shapes Vector
itself. All of the objects in shapes Vector will be
serialized automatically. The Vector class implements a
couple of interfaces, including Serializable, so you can
pass shapes Vector to the writeObject
method. The Rectangle, Oval, and
Line objects in shapes implement the
Shape interface, which itself extends the
Serializable interface. Therefore, all the
Rectangle, Oval, and Line
objects in the shapes Vector are also serializable.
The objects are serialized to the file indicated by
filename.
Open
The Open menu item is connected to the open private method. It does
the opposite of what the save method does. It retrieves the objects
previously saved into the file. It does so by using the readObject
method of the ObjectInputStream class. The readObject
returns an Object object, so in the open method it must be cast to
Vector.
The signature and the body of the open method are given below.
private void open() {
try {
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream(filename));
panel.shapes = (Vector) in.readObject();
panel.repaint();
}
catch(Exception e) {
e.printStackTrace();
}
}
Exit
The Exit menu item makes the application exit by calling the following simple line of code.
System.exit(0);
Conclusion
You have just seen how BrainyDraw demonstrates polymorphism and object serialization in practice. Polymorphism is shown by the draw method in the Shape interface which is overridden in the Rectangle, Oval, and Line classes. When the draw method in a Shape object is called, Java knows which draw method to call. As a result, the object can draw itself correctly.
BrainyDraw also demonstrates how easy it is to do object
serialization in Java. In the save method, you saw how to write
objects to a file. In the open method, you saw how to read the objects
back. All of these are done with one single method call, the
writeObject of the ObjectOutputStream and
the readObject of the ObjectInputStream. The
writeObject method not only serializes the object passed
to it, but all objects referenced by the passed object. To be
serializable, the object passed and all other objects referenced by it
must implement the Serializable interface.
Budi Kurniawan is a senior J2EE architect and author.
Return to ONJava.com.