CS46A Lab

Interfaces

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.

In this lab, you will work in groups of two.

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. Make a project with the classes Car, CarViewer, CarComponent from chapter 3. 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 to your lab report.

  2. Now add this House class to your 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.

  3. If you have a few minutes to spare, rename the CarComponent class to SuburbanScene.

    (Note for Spring 2010: Update to use Netbeans and show off refactoring)

  4. 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.

  5. 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 CarComponent (or, if you renamed it, SuburbanScene) as follows:

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

  6. 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?

  7. 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?

  8. 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 the code for all classes to your lab report.

  9. 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.

    (I mention this here because this kind of thing may be useful for your final project.)

  10. Add a static variable
    private static Random generator = new Random();

    to the SuburbanScene class.

    Make the scene more random. Randomly generate the x and y positions of the cars, houses, and balls, so that x < getWidth() and y < getHeight().

  11. Make the scene even more random. Generate a random value between 0 and 3. If it is 0, add a new car. If it is 1, add a new house. If it is 2, add a new ball. If it is 3, add a new dog.

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 = x + 5, 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. It is a bit tedious to observe the movement. If you minimize and show the window, the contents should have moved. Try it a few times to convince yourself that those cars are moving. We'll improve that in the next section.

    Attach the code for all classes to your lab report.

Part C. Using a Button for Moving

  1. In the CarViewer class, add a JButton to the frame, like this:
    JButton button = new JButton("Move");
    frame.add(button, BorderLayout.NORTH);

    The JButton class is in the javax.swing package, and BorderLayout is in java.awt.

    Compile and run your program. Where is the button?

  2. The buttons in the book's sample program look prettier because they don't fill the entire area. That is achieved by putting the button into a JPanel and then adding the JPanel to the North. Implement this enhancement if you have time.
  3. Click the button. What happens?
  4. Of course, nothing happens—we didn't tell the button what to do when it is clicked. Add this class below the CarViewer class, inside CarViewer.java.
    class MoveListener implements ActionListener
    {
       public void actionPerformed(ActionEvent event)
       {
          System.out.println("move");
       }
    }

    Compile and run the program. What happens when you click the button?

  5. Of course, nothing happens—we didn't tell the button about the MoveListener. In the main method, construct an object listener of your MoveListener. Then call
    button.addActionListener(listener);

    Compile and run the program. What happens when you click the button?

  6. We don't want the program to tell us "move", we want it to move those moveable objects. Recall what you did in step B5. By minimizing and showing the frame, you triggered the repaint method, which calls paintComponent on all components in the frame, which then calls move and draw. So, we want to call
    frame.repaint();

    in the actionPerformed method of the MoveListener. Replace System.out.printl("move") with frame.repaint(). Compile. What happens? Why?

  7. The frame variable isn't in the scope of the actionPerformed method. To fix this, move the entire MoveListener class inside the main method, just before the declaration of the listener variable. Compile. What happens?
  8. That error message is rather unfortunate and the result of some poor decisions in the design of the Java language. To make it go away, simply add the keyword final to the declaration of the frame variable.
    final JFrame frame = new JFrame();

    Now compile and run your program. What happens when you click the button a few times?

    Attach the code for all classes to your lab report.

  9. It is a bit tedious to keep clicking that button. You can do better by using a javax.swing.Timer.

    Add a Timer like this:

    Timer t = new Timer(100, listener); // 100 milliseconds between actions
    t.start();

    Run your program. What happens?

    (To make the car movement prettier, change x = x + 5 to x++.)

  10. Change your program so that the timer only starts when the button is clicked. You will need two listeners, one for the button and one for the timer. In the button listener, call t.start().
  11. For extra bragging rights, can you make those balls move realistically, so that they bounce?

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 CarComponent, 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?