Lab 4: Car Class and Test¶
In this lab, you will implement a Car
class and corresponding CarTest
.
You will learn about non-static attributes and methods.
Learning Objectives¶
After completing this lab, you should be able to:
- Interpret a UML class diagram and stub out the corresponding code.
- Write tests that create objects and run both static and non-static methods.
- Incrementally implement and test non-static methods from stubs.
Step 1. Getting Started¶
Do the following steps in VS Code:
- Create a
lab04
folder under yoursrc/
folder. - Add two new files:
Car.java
andCarTest.java
. - Write the
package
andpublic class
statements. - Write a Javadoc comment with your name and date.
When finished, your Car.java
should look like this:
Step 2. UML Class Diagram¶
Based on the diagram below, declare each attribute and write a stub for each method.
Do not implement any methods yet, but make the code compile.
For example, if a method returns a String
, write return "";
as a stub (placeholder).
Show your code to the instructor or TA before proceeding to the next step.
Note
The brake()
method is void
(does not return a value).
The word void
is usually omitted in UML class diagrams.
Step 3. Constructor, Getters¶
The purpose of a constructor is to initialize the attributes of this
object.
Notice the Car
class has three attributes: make
, speed
, and year
.
The constructor has two parameters: make
and year
.
Use the parameters to initialize this
object's make
and year
.
Initialize this
object's speed
to zero.
Then write the Javadoc comment for the constructor.
Note
When writing documentation for constructors, do not use the word create.
Constructors do not "create" objects; the new
operator creates an object.
Once an object has been created, the constructor is called to initialize the object.
The purpose of a getter method is to return the current value of an attribute.
Notice that the attributes are private
, meaning they can be accessed only in the Car
class.
Other classes, like CarTest
, will need to use the public
getters to access the attributes.
Implement each getter by returning the corresponding attribute.
Javadoc comments are not required for most getter methods. However, a Javadoc comment is required if a getter does more than simply return an attribute.
Step 4. Test the Constructor¶
Before implementing the other methods in Car.java
, let's make sure the constructor and getter methods work correctly.
Open CarTest.java
We will not use unit tests for this lab, but a driver type test file.
Add the following code to CarTest.java
:
Setup a main method
public static void main(String[] args) {
}
-
Read through the code below and identify each of the following: - the creation of two car objects - calls to all three getters for the first car - calls to all three getters for the second car
-
Copy the code to your main:
System.out.println();
System.out.println("Testing Car Constructor()");
Car car1 = new Car("Nissan", 2003);
Car car2 = new Car("Tesla", 2024);
System.out.println("Car 1 make should be Nissan | " + car1.getMake());
System.out.println("Car 1 speed should be 0.0 | " + car1.getSpeed());
System.out.println("Car 1 year should be 2003 | " + car1.getYear());
System.out.println("Car 2 make should be Tesla | " + car2.getMake());
System.out.println("Car 2 speed should be 0.0 | " + car2.getSpeed());
System.out.println("Car 2 year should be 2024 | " + car2.getYear());
System.out.println();
Step 5. Accelerate and Brake¶
The accelerate()
method increases the current speed of the car by 5 miles per hour.
After increasing the speed, accelerate()
returns a string describing the change.
Use the format "15.0 + 5.0 = 20.0"
where 15.0
is the old speed and 20.0
is the new speed.
Note
The String.format()
method is useful for formatting double
values.
For example, you can format the speed values to one decimal place as follows:
return String.format("%.1f + 5.0 = %.1f", speed - 5, speed);
Your code must not allow the speed to increase above 150 miles per hour.
If the speed is already 150, and accelerate()
is called, the speed should remain 150.
In that case, the method should return the string "150.0 + 0.0 = 150.0"
.
The brake()
method should decrease the current speed by 5 miles per hour.
Your code must not allow the speed to decrease below 0 miles per hour.
Write Javadoc comments for accelerate()
and brake()
before moving on to the next step.
Step 6. Test Driving the Car¶
Add the following code to CarTest
:
System.out.println("Testing Car.accelerate() and brake()");
Car car = new Car("Nissan", 2003);
String result = car.accelerate();
System.out.print("\n0.0 + 5.0 = 5.0 | ");
System.out.print(car.getSpeed());
car.brake();
System.out.print("\n5.0 - 5.0 = 0.0 | ");
System.out.print(car.getSpeed());
System.out.println("\nTesting limits of speed");
Car car3 = new Car("Nissan", 2003);
// brake when already 0
car3.brake();
System.out.print("Car3 Braking at 0.0 speed should still be 0.0 | ");
System.out.print(car3.getSpeed());
// accelerate to 150
String results = "";
for (int i = 0; i < 30; i++) {
results = car3.accelerate();
}
System.out.println("\n145.0 + 5.0 = 150.0 | " + results);
System.out.print("Car3 speed should be 150 | ");
System.out.print(car3.getSpeed());
// try to go faster
results = car3.accelerate();
System.out.print("\n150.0 + 0.0 = 150.0 | " + results);
System.out.print("\nCar3 speed should still be 150 | " + car3.getSpeed());
This test should do the following:
- Create a
Car
object. - Call the
accelerate()
method.- Test that the method returned
"0.0 + 5.0 = 5.0"
. - Test that the
Car
object's speed is now 5.
- Test that the method returned
-
Call the
brake()
method.- Test that the
Car
object's speed is now 0.
- Test that the
-
Create a
Car
object. - Call the
brake()
method.- Assert that the
Car
object's speed is 0.
- Assert that the
- Call the
accelerate()
method 30 times, and then:- Assert that the method returned
"145.0 + 5.0 = 150.0"
. - Assert that the
Car
object's speed is now 150.
- Assert that the method returned
- Call the
accelerate()
method one more time.- Assert that the method returned
"150.0 + 0.0 = 150.0"
. - Assert that the
Car
object's speed is still 150.
- Assert that the method returned
Run CarTest again and make sure the tests passes.
Step 7. The equals() method¶
The purpose of the equals()
method is to determine if two objects are equivalent.
Replace your equals()
method with the following solution:
@Override
public boolean equals(Object obj) {
if (obj instanceof Car) {
Car car = (Car) obj;
return car.make.equals(this.make) && car.year == this.year;
}
return false;
}
The above code:
- Overrides the default implementation of
equals()
.- By default,
equals()
returns true ifthis
andobj
are the same object.
- By default,
- Checks if
obj
, which can be any type of object, is actually aCar
object.- If not, then
equals()
returns false at the end.
- If not, then
- Declares a
Car
variable to reference the same object asobj
. - Determines whether
car
has the same make and year asthis
object.
Note
Javadoc comments are not required for @Override
methods, because the
documentation is already defined by the default implementation.
In VS Code, you can hover the mouse over a method's name to see the documentation.
Step 8: The toString() method¶
The purpose of the toString()
method is to return a string that represents the object.
Replace your toString()
method with the provided solution:
@Override
public String toString() {
return String.format("A %d %s that is going %.1f mph",
year, make, speed);
}
The above code:
- Overrides the default implementation of
toString()
.- By default,
toString()
returns the class name and the memory location of the object. For example,"labs.lab04.Car@5b6f7412"
.
- By default,
- Uses the
String.format()
method to build a string based on the object's attributes.
Step 9. Final Test and Submit¶
Add the following test code to CarTest
:
System.out.println();
System.out.println("\nTesting equals and toString");
// Test car equals
Car car4 = new Car("Nissan", 2003);
Car car5 = new Car("Nissan", 2003);
Car car6 = new Car("Honda", 2003);
Car car7 = new Car("Nissan", 2024);
System.out.print("\ncar4 == car5 yields true | ");
System.out.print(car4.equals(car5));
System.out.print("\ncar4 != car6 yields false | ");
System.out.print(car4.equals(car6));
System.out.print("\ncar4 != car 7 yields false | ");
System.out.print(car4.equals(car7));
Object obj = "car8";
System.out.print("\ncar4 is not car8 object yields false | ");
System.out.print(car4.equals(obj));
// Test car toString
Car car9 = new Car("Nissan", 2003);
System.out.println("\nA 2003 Nissan that is going 0.0 mph");
System.out.println(car9.toString());
// Test car toString
Car car10 = new Car("Toyota", 2010);
System.out.println("\nA 2010 Toyota that is going 0.0 mph");
System.out.println(car10.toString());
Note:
1. car4
has the same make and year.
2. car5
has a different make.
2. car6
has a different year.
4. "car7"
(in quotes) is a string.
Calling the equals()
method four times is necessary to cover all the branches.
In contrast, the toString()
method does not have any branches.
So only one method call is necessary to get full coverage of toString()
.
As a final step, click the green play button next to public class CarTest
.
Submit both Car.java
and CarTest.java
inside a lab04 folder to Gradescope.
Points will be allocated as follows:
Criterion | Points | Details |
---|---|---|
Compile | 0 pts | Success Required |
CompileOfficialTests | 0 pts | Success Required |
Style | 0 pts | Success Required |
OfficialTests | 10 pts | Partial Credit Possible |