M/F 2:30-3:20   
in Philips 101

CS 1130: Transition to OO Programming

Spring 2015

Abstract Classes

There is a PDF version of these instructions, if you would prefer to have that instead.

This lab introduces you to the useful software-engineering concepts of an "abstract class" and an "abstract method". The topic is covered in Section 4.7 of the class text and on lesson page 4-5 of the ProgramLive CD. We summarize the material below (in the section Introduction to Abstract Classes), but your lab instructor will present the concepts to you (which are quite simple) if you ask.

This lab also gives you some more practice in reading Java programs and, in the context of this lab, modifying them to draw some shapes in a JFrame.

Requirements For This Lab

There are several files necesssary for this lab. You should create a new directory on your hard drive and download the following five files into this directory:

To use these files, open them all up in DrJava and compile them. In the Interactions Pane, create an instance of DemoShape with the command:

  DemoShapes d = new DemoShapes();

A figure like that on the right should appear in the window. The output in the Java console describes seven shapes that are drawn in the window.

This lab will be a combination of written exercises and modifying the files above. As in the last lab, there is no JUnit test because you will be able to test al of your code visually with DemoShapes. Furthemore, to simplify the lab submission, we are not asking you to show off your .java files. Instead, we have given you space in this lab to write down what you have done on paper (starting in the section Lab Exercises). You should show this paper to your instructor when you are done.

As always, you should try to finish the lab during your section. However, if you do not finish during section, you have until the beginning of lab next week to finish it. You should always do your best to finish during lab hours; remember that labs are graded on effort, not correctness.


Understanding the Graphics Window

Before we get you started on this lab, we should first describe a bit how DemoShapes works. Notice that class DemoShapes extends JFrame, so that DemoShapes is associated with a window on the monitor. You saw a similar example to this in the lab last week.

It is possible to can draw pictures inside a JFrame. The JFrame window is a two-dimensional plane of pixels. The pixels are indexed by integer coordinates, as follows:


  (0,0)  (1,0)  (2,0)  ...
  (0,1)  (1,1)  (2,1)  ...
  (0,2)  (1,2)  (2,2)  ...
  ...

In a pixel coordinate (x,y), x is the horizontal coordinate; it increases from left to right. Similarly, y is the vertical coordinate; it increases from top to bottom.

The method paint(Graphics g) is inherited from JFrame. Whenever the system knows that it needs to redraw the window, it calls this method paint, with a suitable Graphics object g as its argument. The object named in g contains several methods that can be used to draw, and these are the ones used in the program.

Method Description
g.drawLine(x1, y1, x2, y2); Draw a line from pixel (x1,y1) to (x2,y2)
g.drawRect(x, y, w, h); Draw a rectangle with upper left corner (x,y), width w, and height h
g.getColor() Yields: the color currently being used to draw
g.setColor(c); Set the color to draw with to c (which is of class Color)


Introduction to Abstract Classes

Problem 1: Preventing a Class from Being Instantiated

In some situations, we do not want programmers to instantiate (create an instance of) a particular class; rather we choose to define the class only so that it can serve as a default superclass of other classes. This is ideal for containers, when the container can only values that all have the same type.

For example, we might want to have a class Shape to serve as superclass for a number of "real" subclasses, like Parallelogram or Rhombus. We want to have Parallelogram and Rhombus objects around, but not generic "Shape" objects. However, so far we have not learned of a way to prevent programmers from instantiating (creating instances of) a class.

Solution: The correct approach is is to change the class to an abstract class; by definition and an abstract class cannot be instantiated. To do this, change the first line of the definition of the class, say class C, from

public class C {         to         public abstract class C {

Purpose of making a class abstract:

You make a class abstract so that it cannot be instantiated (one cannot create an instance of it).


Problem 2: Forcing Programmers to Override a Method in a Subclass

In the abstract class Shape, the method drawShape is defined only so that it can be overridden by "real-shape" subclasses. We do not want programmers to call the drawShape method in Shape, and in fact, such a method would not make sense. How do we draw it if we do not know exactly what the shape is supposed to be?

Instead, the programmers should write overriding methods in the appropriate subclasses, which contain the drawing code for the specific shapes. However, we cannot force them to override the method in Shape, and if they do not, the drawShape method in Shape will be called. Therefore, we want something that will force programmers to implement a new drawShape method for each subclass of Shape.

Solution: The correct approach is to change drawShape to an abstract method, because abstract methods by definition must be overridden. To do this, change drawShape as shown below. There are two changes: (1) the keyword abstract is inserted and (2) the method body, including the enclosing braces, is replaced by a semicolon.

public void drawShape(...) { ... }

to

public abstract void drawShape(...);

Purpose of making a method abstract:

You make a method in an abstract class abstract so that it cannot be called and must be overridden.


Lab Exercises

Task 1: Make class Shape abstract

In file DemoShapes.java, place the following statement in method paint, just before the declaration (and initialization) of variable h:

   Shape s0 = new Shape(5,5);

Compile the program. Write down what this statement does.





In the file Shape.java, place keyword abstract just before keyword class in the class definition, so that the third line of the file looks as follows:

   public abstract class Shape {

You have made the class into an abstract class. Compile the program again. Do you get an error message? Write down the error message and explain in a few words why there is an error.





Now delete the statement that you placed in file DemoShapes.java above and compile again. You should no longer have an error message.


Task 2: Make method drawShape of class Shape abstract

In file Shape.java, change method drawShape to the following:

   public abstract void drawShape(Graphics g);

Remember to replace the body {} by a semicolon. You have made this method into an abstract method. Compile the program; it should still compile.

Open file Parallelogram.java and comment out method drawShape (put /* before the method and */ after the method). Try to compile the program. Do you get error messages? Write on your paper the rror message that deals with class Parallelogram. Write a few words explaining what the error is.





Remove the comment symbols, so that drawShapes is again defined in Parallelogram. Try compiling the program again just to be sure that you removed them correctly.


Task 3: Add Some Arms

Class Shape is designed to be the root of all classes that draw a shape. We have the following hierarchy:

Object -> Shape -> Parallelogram -> Rhombus -> Square
because a square is a rhombus with 90-degree angles, a rhombus is a parallelogram all of whose sides are of equal length, and a parallelogram is a shape.

The shape that appears when a new DemoShapes is created looks almost like a person. It is drawn using methods of instance g of class Graphics that is attached to the JFrame object that opens. The only methods you need from Graphics are setColor and getColor. You will do most of your work in this task using the Shape classes.

You will give the person arms. All the changes you will make will be in class DemoShapes. Read through method paint of DemoShapes.

But before you do anything else, you should comment out the code that produces the two black lines (in DemoShapes).

Hint: look for where the color is set to black.

How to Draw the Arms

Each arm is should be a green rectangle that is 60 pixels long and 20 pixels high. Its leaning factor (field d of class Parallelogram) is 0, which means that it is a rectangle. The leaning factor is defined on Lesson page 4.4 of the ProgramLive CD (see also the comment at the beginning of class Parallelogram), but you really do not have to read about it. Later, when you get the program going with leaning factor 0, you can try a different leaning factor, say 15, and see what it looks like.

The arms should be attached at the top right and top left of the square that makes up the body. The tops of the arms should be parallel to the top line of the square.

In writing the code that draws these rectangles, use the variables that are defined at the top of method paint. Also, use variables to contain all the constants that you need, as we did in method paint. You may have to move the whole figure to the right (by changing the value of variable x) so that you can see the whole picture. You must use class Parallelogram; you may not use method drawRect in class Graphics.

Hint: to figure out the coordinates for the arms, look at the positioning of the green square. For debugging purposes, you may want to include System.out.println statements to see important values for the objects you create, like we did. The command System.out.println() outputs the given text (or toString() method if an object) to the console.

When you are finished, write on your paper the sequence of statements that you added to method paint.