CS46A Lab

Implementing Classes

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

See lab 1 for reporting instructions.

The steps tagged with this icon are optional. Do them if you have time and want to practice more.

Implementing Classes

A. A Car class

  1. Your task is to implement a Car class that simulates a car. The class has methods for adding gas, checking the gas level, driving for a number of miles, and checking the odometer (i.e., total miles driven).

    Let's deal with adding gas first.

    What should the following program print?

    /**
       This program tests the Car class.
    */
    public class CarTester
    { 
       public static void main(String [] args)
       { 
          Car myHybrid = new Car(); 
          myHybrid.addGas(20); 
          myHybrid.addGas(10);
          double gasLeft = myHybrid.getGasInTank();       
          System.out.print("Gas left: ");
          System.out.println(gasLeft);
       }
    }
  2. In BlueJ, make a project car and classes CarTester and Car.

    In the tester program, add a line

          System.out.println("Expected: ...");

    where you replace ... with the value that you expect (from step 1).

    Start out with this Car class:

    /** 
        A car can drive and consume fuel. 
    */ 
    public class Car 
    { 
       /** 
          Constructs a car. 
       */ 
       public Car() 
       {
       }
    
       /** Adds gas to the tank. 
           @param amount the amount of fuel to add 
       */ 
       public void addGas(double amount) 
       { 
       } 
    
       /** 
           Gets the amount of gas left in the tank. 
           @return the amount of gas 
       */ 
       public double getGasInTank() 
       { 
          return 0;
       }
    }  

    Now run the tester program. (Right-click on the CarTester class and select main.)

    What output do you get? Why don't the actual values match the expected values?

  3. To fix this, each Car object needs to have a way of remembering the amount of gas in the tank. An object uses instance variables to remember information. Add an instance variable as follows:
    /** 
        A car can drive and consume fuel. 
    */ 
    public class Car 
    { 
       private double gasInTank;
       /** 
          Constructs a car. 
       */ 
       public Car() 
       {
          gasInTank = 0;
       }
    
       /** Adds gas to the tank. 
           @param amount the amount of fuel to add 
       */ 
       public void addGas(double amount) 
       { 
       } 
    
       /** 
           Gets the amount of gas left in the tank. 
           @return the amount of gas 
       */ 
       public double getGasInTank() 
       { 
          return gasInTank;
       }  
    }

    Now run the tester program.

    What output do you get? Why don't the actual values match the expected values?

  4. We still need to implement the addGas method. Adding gas to a car is pretty much the same as depositing money in a bank account. Look into section 3.5 of your textbook and locate the deposit method.

    What is its code?

  5. Here, balance is an instance variable in the BankAccount class. Each bank account remembers its balance.

    What is the analog of balance in our Car class?

  6. Looking again at the deposit method, how do you implement the addGas method?
  7. Now run the tester again. What happens?
  8. We are done with one part of our requirements. Let's move on to driving.

    Add these calls to the end of the CarTester class:

          myHybrid.drive(200); 
          myHybrid.drive(100);
          double milesDriven = myHybrid.getMilesDriven();       
          System.out.print("Miles driven: ");
          System.out.println(milesDriven);

    What do you expect these lines to print?

  9. Compile the tester. What happens? Why?
  10. Add these two methods to the Car class:
       /** 
           Drives a certain amount, consuming gas. 
           @param distance the distance driven 
       */ 
       public void drive(double distance) 
       { 
       } 
    
       /** 
           Gets the total miles driven by this car. 
           @return the miles driven
       */ 
       public double getMilesDriven() 
       { 
          return 0;
       }

    Compile and run the tester. What happens? Why?

  11. The car needs to remember how many miles it has driven.

    What do you need to add to the class so that it can remember?

  12. Add a milesDriven instance variable, set it to zero in the constructor, return it in the getMilesDriven method, and update it in the drive method.

    What changes did you make to the code?

  13. Compile and run the tester. What happens?
  14. Now make another tester with this code:
    /**
       This program tests the Car class.
    */
    public class CarTester2
    { 
       public static void main(String [] args)
       { 
          Car myHybrid = new Car(); 
          myHybrid.addGas(20); 
          myHybrid.drive(200); 
          myHybrid.drive(100); 
          double gasLeft = myHybrid.getGasInTank();       
          System.out.print("Gas left: ");
          System.out.println(gasLeft);
       } 
    }

    Compile and run this tester. What happens?

  15. It says that the car is a hybrid, but even a hybrid must consume some gas. How much depends on the fuel efficiency.

    Let's say the car has a fuel efficiency of 50 miles per gallon. How much fuel does it consume when it drives 300 miles?

  16. How much fuel should be left in the gas tank after starting with 20 gallons and driving 300 miles? Add a System.out.println("Expected: ...") line to the end of CarTester2.

    What line did you add?

  17. Of course, the tester can't pass yet. Which method of the Car class do you need to modify so that the car behaves correctly?
  18. Add the line
    double gasConsumed = distance / efficiency;

    to the drive method in the Car class.

    Compile. What error do you get?

  19. Add an instance variable
    private double efficiency;

    to the top of the Car class.

    Compile and run CarTester2. What happens?

  20. Set efficiency to 50 in the constructor.

    Compile and run CarTester2. What happens?

  21. That's all good, but not every car has a fuel efficiency of 50 miles per gallon. Each car should have its own value for the fuel efficiency. It can be passed to the constructor, like this:
    Car myGuzzler = new Car(10);

    Change the constructor of the Car class to

       /** 
          Constructs a car with a given fuel efficiency. 
          @param anEfficiency the fuel efficiency of the car 
       */ 
       public Car(double anEfficiency) 
       {
          gasInTank = 0;
          milesDriven = 0;
          efficiency = anEfficiency;
       }

    Now compile CarTester2. What happens? Why?

  22. How do you fix CarTester2 so that it again passes the test?

B. A Car with Alice and Netbeans

  1. It's no fun that we don't get to see the car. Let's do it in Alice. Download this NetBeans project and unzip. Start Netbeans (not Alice!) and load the project. Select Run -> Set Main Project -> car and Run -> Run Main Project from the menu. What happens?
  2. Ok, no car yet. In the Projects tab, open up at car -> Source Packages -> <default package>. Right-click on <default package> and select New -> Java Class. Fill in Car. When the class is generated, remove all of the generated code and paste in the Car class that you wrote in part A.

    Then change the first line from class Car to class Car extends RedRover

    (Alice 3.1 doesn't have a model of an actual car, so I picked this one instead. You'll see presently how it looks.)

    In myFirstMethod inside the Scene class, add these lines:

    Car myRover = new Car(50);
    myRover.setVehicle(this); // Needed to make the car appear in the scene

    Then run the program.

    What happens?

  3. Now, let's drive the car. Add the following call to the end of myFirstMethod:
    myRover.drive(100);

    Then run the program.

    What happens?

  4. Of course, it doesn't do anything interesting yet. In the drive method of Car, add a call
    move(MoveDirection.FORWARD, distance / 100);

    This will make the car move. We divide by 100 so that the car doesn't leave the screen.

    Run the program. What happens?

  5. Now let's find out how much gas is left. Add
    myRover.say("Gas: " + myRover.getGasInTank());

    at the end of myFirstMethod.

    Run the program. What happens?

  6. It would be nicer if the car moved sideways. For that, paste the following line below the call to myRover.setVehicle(this);
    myRover.setOrientationRelativeToVehicle(new Orientation(0.0, Math.sqrt(2) / 2, 0.0, 1.0));

    Run the program. What happens?

C. An Optional Gas Gauge

  1. Ok, that's nice. But it would be even nicer to have an actual gas gauge instead of a talking car. I couldn't find a gas gauge in Alice's collection of shapes, but hey, there is always the ColaBottle.

    Add this import statement to Car:

    import org.lgna.story.resources.prop.ColaBottleResource;

    Add this instance variable to Car:

    private Prop gauge;

    Add this code to the Car constructor:

    gauge = new Prop(ColaBottleResource.COLA_BOTTLE);
    gauge.setVehicle(this);

    Add this line to the drive method:

    gauge.turn(TurnDirection.BACKWARD, gasConsumed / 10);

    As always, don't worry about the Alice magic. You will never be asked to memorize or create it.

    What happens when you run the program?

  2. Isn't it grand? The only flaw is that the car moves first and then the gauge is adjusted. In Java, statements are executed in sequence. It is possible to make both movements together, but that goes well beyond what you'll learn in your first CS course. And it's pretty ugly in Java. (Other programming languages make it easier.) Don't peek if you are squeamish...

    Ok, you peeked. Here is the outline.

    DoTogether.invokeAndWait(
       new Runnable() 
       {
          public void run() 
          {
             move(MoveDirection.FORWARD, distance / 100);
          }
       },
       new Runnable() 
       {
          public void run() 
          {
             gauge.turn(TurnDirection.BACKWARD, gasConsumed / 10);
          }
       });

    You also need to declare the variables used inside the run methods as “final”:

    public void drive(final double distance)
    {
       final double gasConsumed = distance / efficiency;
       ...
    }

    I won't explain this—you'll learn more about it in CS151.

    Try it out. How is this different?

  3. The gas gauge now goes backward when the car consumes gas. Which other method should be updated to make the gas gauge show the gas level?
  4. Fix that method. What is your code?
  5. What happens when you run the program now?