Menu
Coddy logo textTech

Vehicle Rental Service

Part of the Object Oriented Programming section of Coddy's Java journey — lesson 87 of 87.

For this final challenge, you'll build a Vehicle Rental Service system that brings together all the OOP concepts you've mastered throughout this course.

Your system should model a fleet of different vehicle types. Consider a base Vehicle class with common properties like license plate, brand, and daily rental rate. Subclasses such as Car, Motorcycle, and Truck can extend this with specific attributes like number of seats, engine size, or cargo capacity.

Interfaces can define rental behaviors: a Rentable interface might declare methods like rent(), returnVehicle(), and calculateCost(int days). Different vehicle types could implement pricing strategies differently, making this a perfect use case for polymorphism.

Think about how a RentalAgency class manages the fleet using composition.

Custom exceptions like VehicleNotAvailableException can handle edge cases.

You might apply the Factory pattern to create vehicles or use enums for vehicle categories and rental status.

This challenge tests your ability to design a complete, well-structured OOP system. Good luck!

challenge icon

Challenge

Easy

Let's build a complete Vehicle Rental Service that brings together everything you've learned about OOP! You'll create a system that manages a fleet of different vehicle types, handles rentals with proper validation, and uses a factory pattern to create vehicles.

You'll organize your code across seven files:

  • RentalStatus.java: Create an enum representing the rental status of a vehicle. Include values AVAILABLE, RENTED, and MAINTENANCE. Add a private field description (String), a constructor, and a getter. Available should have description "ready to rent", rented should have "currently in use", and maintenance should have "under service".
  • VehicleNotAvailableException.java: Create a custom exception that extends Exception. The constructor should accept a license plate and create a message in the format Vehicle not available: [licensePlate].
  • Rentable.java: Create an interface defining rental operations. Declare methods rent() returning a boolean, returnVehicle() returning a boolean, and calculateCost(int days) returning a double.
  • Vehicle.java: Create an abstract base class for all vehicles. Include private fields for licensePlate (String), brand (String), dailyRate (double), and status (RentalStatus, initialized to AVAILABLE). The constructor takes licensePlate, brand, and dailyRate. Include appropriate getters and a setter for status. Declare an abstract method getVehicleType() returning a String. Implement a getDetails() method that returns "[licensePlate] [brand] ([vehicleType]) - $[dailyRate]/day - [status description]" with dailyRate formatted to 2 decimal places.
  • Car.java: Create a class extending Vehicle and implementing Rentable. Add a private field seats (int). The constructor takes licensePlate, brand, dailyRate, and seats. Implement getVehicleType() to return "Car". For rent(), if status is AVAILABLE, set it to RENTED and return true; otherwise return false. For returnVehicle(), if status is RENTED, set it to AVAILABLE and return true; otherwise return false. For calculateCost(int days), return dailyRate times days.
  • Motorcycle.java: Create a class extending Vehicle and implementing Rentable. Add a private field engineCC (int). The constructor takes licensePlate, brand, dailyRate, and engineCC. Implement getVehicleType() to return "Motorcycle". Implement the Rentable methods the same way as Car, but calculateCost() should apply a 10% discount (multiply by 0.9).
  • RentalAgency.java: Create a class that manages the vehicle fleet using an ArrayList. Include methods:
    • addVehicle(Vehicle vehicle) - adds a vehicle to the fleet
    • findVehicle(String licensePlate) - returns the Vehicle or null
    • rentVehicle(String licensePlate, int days) - finds the vehicle, attempts to rent it (casting to Rentable), and returns the rental cost. Throw VehicleNotAvailableException if the vehicle isn't found or if rent() returns false
    • returnVehicle(String licensePlate) - finds and returns the vehicle, returning true on success
    • getAvailableVehicles() - returns an ArrayList of vehicles with AVAILABLE status
  • Main.java: Build your rental service! You'll receive a comma-separated string of commands:
    • ADD_CAR:licensePlate:brand:dailyRate:seats
    • ADD_MOTORCYCLE:licensePlate:brand:dailyRate:engineCC
    • RENT:licensePlate:days
    • RETURN:licensePlate
    • AVAILABLE

    Process each command and print:

    • ADD_CAR/ADD_MOTORCYCLE: Added: [brand] ([vehicleType])
    • RENT: Rented [licensePlate] for [days] days - Total: $[cost] (cost to 2 decimals), or Error: [exception message]
    • RETURN: Returned: [licensePlate] or Return failed: [licensePlate]
    • AVAILABLE: Print Available vehicles: followed by each available vehicle's details on separate lines

    After all commands, print --- Fleet Summary --- followed by each vehicle's details.

You will receive one input: the comma-separated command string.

For example, with input ADD_CAR:ABC-123:Toyota:50.00:5,ADD_MOTORCYCLE:XYZ-789:Honda:40.00:600,RENT:ABC-123:3,RENT:ABC-123:2,AVAILABLE,RETURN:ABC-123, your output would be:

Added: Toyota (Car)
Added: Honda (Motorcycle)
Rented ABC-123 for 3 days - Total: $150.00
Error: Vehicle not available: ABC-123
Available vehicles:
XYZ-789 Honda (Motorcycle) - $40.00/day - ready to rent
Returned: ABC-123
--- Fleet Summary ---
ABC-123 Toyota (Car) - $50.00/day - ready to rent
XYZ-789 Honda (Motorcycle) - $40.00/day - ready to rent

This challenge combines inheritance (Vehicle hierarchy), interfaces (Rentable), enums (RentalStatus), custom exceptions, polymorphism (treating different vehicle types uniformly), and composition (RentalAgency managing vehicles) into a complete rental service system!

Cheat sheet

This final challenge combines multiple OOP concepts into a complete system. Here are the key patterns and techniques:

Enums with Fields

Enums can have fields, constructors, and methods:

public enum RentalStatus {
    AVAILABLE("ready to rent"),
    RENTED("currently in use");
    
    private String description;
    
    private RentalStatus(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

Custom Exceptions

Create domain-specific exceptions by extending Exception:

public class VehicleNotAvailableException extends Exception {
    public VehicleNotAvailableException(String licensePlate) {
        super("Vehicle not available: " + licensePlate);
    }
}

Abstract Base Classes with Interfaces

Combine abstract classes for shared implementation with interfaces for behavior contracts:

public abstract class Vehicle {
    private String licensePlate;
    private RentalStatus status;
    
    public abstract String getVehicleType();
    
    public String getDetails() {
        return licensePlate + " " + getVehicleType() + " - " + status.getDescription();
    }
}

public interface Rentable {
    boolean rent();
    boolean returnVehicle();
    double calculateCost(int days);
}

public class Car extends Vehicle implements Rentable {
    public boolean rent() {
        if (getStatus() == RentalStatus.AVAILABLE) {
            setStatus(RentalStatus.RENTED);
            return true;
        }
        return false;
    }
}

Composition for Management

Use composition to manage collections of objects:

public class RentalAgency {
    private ArrayList<Vehicle> fleet = new ArrayList<>();
    
    public void addVehicle(Vehicle vehicle) {
        fleet.add(vehicle);
    }
    
    public double rentVehicle(String licensePlate, int days) throws VehicleNotAvailableException {
        Vehicle vehicle = findVehicle(licensePlate);
        if (vehicle == null) {
            throw new VehicleNotAvailableException(licensePlate);
        }
        Rentable rentable = (Rentable) vehicle;
        if (!rentable.rent()) {
            throw new VehicleNotAvailableException(licensePlate);
        }
        return rentable.calculateCost(days);
    }
}

Polymorphism in Action

Different subclasses can implement interface methods differently:

// Car: standard pricing
public double calculateCost(int days) {
    return dailyRate * days;
}

// Motorcycle: discounted pricing
public double calculateCost(int days) {
    return dailyRate * days * 0.9;
}

Try it yourself

import java.util.Scanner;
import java.util.ArrayList;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Create a RentalAgency instance
        
        // TODO: Split the input by comma to get individual commands
        
        // TODO: Process each command:
        // ADD_CAR:licensePlate:brand:dailyRate:seats -> print "Added: [brand] ([vehicleType])"
        // ADD_MOTORCYCLE:licensePlate:brand:dailyRate:engineCC -> print "Added: [brand] ([vehicleType])"
        // RENT:licensePlate:days -> print "Rented [licensePlate] for [days] days - Total: $[cost]" (2 decimals)
        //                           or "Error: [exception message]" if exception thrown
        // RETURN:licensePlate -> print "Returned: [licensePlate]" or "Return failed: [licensePlate]"
        // AVAILABLE -> print "Available vehicles:" followed by each available vehicle's details
        
        // TODO: After all commands, print "--- Fleet Summary ---"
        // followed by each vehicle's details
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming