CS46A Lab

Interfaces and Inheritance

Copyright © Cay S. Horstmann 2009 Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.

Put the answers to the questions in each step into the lab report. Copy/paste the programs that you write in the lab.

This icon indicates optional tasks. Do those if you have time.

Part A: The Drawable Interface

  1. BlueJ is a nice environment for beginners, but it is time to leave it behind. In this lab, we will use NetBeans.

    We start with the code for the car viewer from Chapter 3. Download and unzip it on your computer. (Remember to actually unzip the file. Some operating systems—which we won't name to protect the guilty—display a zip file as a folder, giving you the illusion that it is unzipped.)

    You'll get a directory carviewer containing a directory src, which contains several Java files. (Some operating systems—which we won't name ...—will make a carviewer directory inside a carviewer directory. That's ok—just keep it in mind in the next step.)

    Start NetBeans and select File → New Project from the menu. In the dialog, select Java and Java Project with Existing Sources.

    Click Next. Give the project a name Lab12A. Click on the Browse button and select the carviewer directory. You want to select the directory containing the src subdirectory, not the src subdirectory itself.

    Click Next.

    Click Add Folder next to the Source Package Folders text area. Select the src folder inside the carviewer folder.

    Click Finish.

    Look at the Projects window at the left side of the NetBeans window. The Lab12A project should be in boldface. If not, right-click and select Set as Main Project

    Select Run → Run Main Project from the menu. What happens?

  2. Modify the program so that it can draw an arbitrary number of cars:

    Run your program to check that it draws the cars. Attach the source code of the CarComponent class to your lab report.

  3. Look at the House class in the project. Modify the CarComponent class so that it also draws houses:

    Run your program to check that it draws the cars and houses. Attach the source code to your lab report.

  4. Right-click on the CarComponent class in the Projects window. Select Refactor->Rename from the menu. Rename the class to to SuburbanScene.

    Open the CarViewer class. What do you notice?

    This smart renaming doesn't happen in BlueJ. It is one of the reasons you want to switch to a real development environment.

  5. We have implemented several classes for drawing shapes: cars, houses, polygons, and so on. They all have something in common: a method
    public void draw(Graphics2D g2)

    Make an interface Drawable that declares a single method draw. Make Car and House implement that interface.

    Compile and run your program to make sure it still works.

  6. That wasn't very exciting. We didn't really make use of the fact that Car and House (and potentially other classes) implement a common interface. Change the SuburbanScene as follows:

    Run your program to check that it draws the cars and houses. Attach the source code of the SuburbanScene class to your lab report.

  7. Look at the paintComponent method again. It should contain code similar to
    for (Drawable d : drawables)
       d.draw(g2);

    How does the compiler know that d (or whatever you called it) has a draw method?

  8. Look at the constructor again. It should contain code similar to
    drawables.add(new Car(30, 40));

    But drawables is not an ArrayList<Car>. It is an ArrayList<Drawable>. Why is it legal to add a Car?

  9. Implement a class Ball that also implements the Drawable interface. Its draw method should call
    g2.setColor(Color.RED);
    g2.fill(new Ellipse2D.Double(x, y, DIAMETER, DIAMETER));
    g2.setColor(Color.BLACK);

    Add a few balls to the drawables array list.

    Attach a screen shot with cars, houses, and balls to your lab report.

  10. For a prettier display, you can use pre-made images instead of laboriously drawing everything with lines and circles. Look at this Dog class for an example. You also need this image in the same directory. Add some dogs to your scene.

Part B: The Moveable Interface

  1. Some objects move, for example cars and balls. Make an interface
    public interface Moveable
    {
       void move();
    }

    Have the Car and Ball classes (but not the House class) implement the Moveable interface in addition to the Drawable interface. Simply add a comma and Moveable to the class declarations. Compile your code. What happens? Why?

  2. You need to provide the method that the interface required. For now, we'll just have cars move horizontally and balls move vertically. That is, in the move method for Car, simply execute x = xLeft++, and for Ball, y--.

    Provide these methods and compile your code. It should now compile without errors.

  3. Of course, nothing is moving yet. In your draw method, make the following change. Before your call to d.draw(g2) (or whatever you called the Drawable), add the lines
    if (d instanceof Moveable) 
    { 
       d.move();
    }

    Compile. What happens? Why?

  4. The compiler refuses to call move on a variable that isn't of type Moveable. Here, we need to use a cast.
    Moveable m = (Moveable) d;

    The cast is safe because we already checked that d instanceof Moveable. Using the cast, make the moveable objects move before they are drawn. Compile to make sure you don't get any errors. What is your paintComponent method now?

  5. We want to see the cars and balls move. This requires a bit of GUI programming that is not part of this course, so I just give you the recipe. Add a Timer like this:
    import javax.swing.Timer;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     ...
    public class CarViewer
    {
       public static void main(String[] args)
       {
          final JFrame frame = new JFrame(); // Add final to the variable declaration      
          ...
          frame.setVisible(true);
          class TimerListener implements ActionListener
          { 
             public void actionPerformed(ActionEvent event)
             {
                frame.repaint();
             }
          }
          ActionListener listener = new TimerListener();
          Timer t = new Timer(100, listener); // 100 milliseconds between actions
          t.start();
       }
    } 

    Run your program. What happens?

    Attach the code for all classes to your lab report.

  6. For extra bragging rights, can you make those balls move realistically, so that they bounce?

Part C: The Measurable Interface

  1. The Measurable interface from the textbook is a bit abstract. The following tasks should help with visualizing it.

    Make the House class implement the Measurable interface. The getMeasure method should return the area of the house (width * height).

    Compile the class to make sure there are no syntax errors. Attach the code to your lab report.

  2. In the SuburbanScene, add an instance variable
    private Measurable largest;

    In the constructor, add a local variable data of type DataSet. Add all houses to the data set, then set largest to data.getMaximum().

    In the paintComponent class, locate the call to d.draw(g2) (or whatever variable you used) and change it to

    if (d == largest) g2.setColor(Color.GREEN);
    d.draw(g2);
    g2.setColor(Color.BLACK);

    Now run your program. What happens? Why?

  3. In chapter 7, you learned an algorithm for computing the largest value in an array list. But here, we didn't need to use that algorithm. Why not?

Part D: Inheritance

  1. Unzip these source files somewhere. In Netbeans, make a project Lab12D containing these files. Look at the ClocksComponent class. In its paintComponent method, add a loop that draws all clocks in the clocks array list. Run the program. What happens?

  2. Now we want to put inheritance to work. It is not so nice that we can't tell whether a clock shows AM or PM. We want to draw the clock with a yellow background in the day and a blue background at night. But we don't want to change the Clock class. Instead, we will use inheritance.

    Make a new class ColoredClock that extends Clock. In the ClocksComponent, change one of the two clocks to ColoredClock. Compile and run. (To make a new class in Netbeans, go to in the Project tree at the left, expand the project, then Source Packages, right-click on the "default package" icon, then select New -> Java class.)

  3. Of course, nothing has changed yet. We need to override the draw method. Add a draw method to the ColoredClock class. To get started, let's draw a yellow background:
    public void draw(Graphics2D g2)
    {
       Color c = Color.YELLOW;
       g2.setColor(c);
       g2.fill(getBounds());
       g2.setColor(Color.BLACK);
    }

    (The getBounds method returns the rectangle bounding the clock.)

    Run your program. What happens?

  4. You got the background for our improved clock, but we lost something important—the clock itself. How do you get the clock to draw itself? Hint: super.
  5. Now we want the background to change depending on the time. Call getHours to get the current hours. If they are between 6 (inclusive) and 18 (exclusive), then draw a yellow background. Otherwise, draw a blue background. Compile and run. What is your code?
  6. Unfortunately, you'd have to wait for a while to test that your code works. We'll fake this as follows. The update method updates the current time. Let's override it so that it adds 12 to the hours:
    public void update()
    {
       super.update();
       hours = (hours + 12) % 24;
    }

    Compile. What happens? Why?

  7. Now use the public interface of the Clock class to fix this method. Compile and run. What happens?

    When you are done, remove that update method (or comment it out by surrounding it with /* and */).

  8. Make the background a circle instead of a rectangle.
  9. You just saw how to use inheritance to change the behavior of an existing class. Now we want to make a clock with a more significant change: to show the time anywhere in the world. (Ok, not quite anywhere. We ignore those places that have fractional offsets such as New Delhi, which is 13 1/2 hours earlier than San Francisco.)

    A WorldClock differs from a Clock in two ways:

    Make a class WorldClock that extends Clock. Add instance variables label and offset. Make a constructor that sets them. Add a new WorldClock("New York", -3) and new WorldClock("London", -8) to the ClocksComponent. Compile and run.

  10. Of course, the label doesn't yet show up. We need to draw it. Make a draw method of WorldClock that contains a call
    Rectangle bounds = getBounds();
    double x = bounds.getX();
    double y = bounds.getY();
    g2.drawString(label, (int) x, (int) y);

    Compile and run. What happens? (If one of the labels doesn't show up, move the clock down a bit.)

  11. Fix the draw method so that it shows the time and the label.
  12. Now we need to adjust for the time offset. Use step 6 as a guidance, but adjust by the time offset for this city, not the fixed value 12. Compile and run your program. Your clocks should now show the correct local time. Attach a screen capture to your lab report.
  13. In the update method, protect against taking the % of a negative number. For example, if it is currently 6:00 in San Francisco, the time in London is 21:00, but (6 - 9) % 24 would be -3. Fix this and test by calling the toString method.
  14. Fix the draw method so that the label shows up below the clock.
  15. Make the world clocks colored. (Hint: This is a one-line change.)