9 Polymorphism

Polymorphism, Abstract Classes, and Interfaces

Objective

  • Declare and implement abstract classes and interfaces.
  • Apply composition, inheritance, and polymorphism.

Exercise 1*

  1. Implement all four classes represented in the hierarchy of the following class diagram.

    Shape+Shape()+area() : floatShape2D#width : float#height : float+Shape2D(float, float)Triangle+Triangle(float, float)+area() : floatRectangle+Rectangle(float, float)+area() : float

A Triangle’s area is 0.5f * height * width and a Rectangle’s area is height * width. Note: Symbols rendered using italics are abstract and # is used for protected field/methods.

  1. Add an interface Scalable with a scale(double) method and realize it using Triangle and Square.

    Scalable+scale(float) : voidTriangle+scale(float) : voidRectangle+scale(float) : void
Solution
Shape.java
public abstract class Shape {
  public Shape() {}
 
  public abstract float area();
}
Shape2D.java
public abstract class Shape2D extends Shape {
  protected float width;
  protected float height;
 
  public Shape2D(float width, float height) {
    this.width = width;
    this.height = height;
  }
}
Scalable.java
public interface Scalable {
  void scale(float factor);
}
Triangle.java
public class Triangle extends Shape2D implements Scalable {
  public Triangle(float width, float height) { super(width, height); }
 
  @Override
  public float area() {
    return 0.5f * height * width;
  }
 
  @Override
  public void scale(float factor) {
    width *= factor;
    height *= factor;
  }
}
Rectangle.java
public class Rectangle extends Shape2D implements Scalable {
  public Rectangle(float width, float height) { super(width, height); }
 
  @Override
  public float area() {
    return height * width;
  }
 
  @Override
  public void scale(float factor) {
    width *= factor;
    height *= factor;
  }
}

Exercise 2*

Virtual Reserve

Create a virtual reserve with different types of animals, each with its unique behaviors.

  1. Use a base class Animal and derived classes like Lion, Bear, Gull, Duck, Crow, and Penguin. Each animal has a name and an age.
  2. Implement polymorphic methods such as eat(), move(), and sound() for each animal.
  3. Define interfaces for walking, flying, swimming, diving, and hibernating animals.
  4. Implement polymorphic methods such as walk(), fly(), swim(), dive(), and hibernate() based on the abilities of each animal.
Solution
Animal.java
public abstract class Animal {
  private String name;
  private int age;
 
  public Animal() {}
  public Animal(String name, int age) {
    this.name = name;
    this.age = age;
  }
 
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
 
  public int getAge() { return age; }
  public void setAge(int age) { this.age = age; }
 
  public abstract void eat();
  public abstract void move();
  public abstract void sound();
}
Diver.java
public interface Diver {
  void dive();
}
Flyer.java
public interface Flyer {
  void fly();
}
Hibernator.java
public interface Hibernator {
  void hibernate();
}
Swimmer.java
public interface Swimmer {
  void swim();
}
Walker.java
public interface Walker {
  void walk();
}
Bear.java
public class Bear extends Animal implements Walker, Swimmer, Hibernator {
  public Bear(String name, int age) { super(name, age); }
 
  @Override
  public void eat() {
    System.out.println(getName() + " the bear is eating fish and berries.");
  }
 
  @Override
  public void move() {
    walk();
  }
 
  @Override
  public void sound() {
    System.out.println(getName() + " the bear growls: GRRR!");
  }
 
  @Override
  public void walk() {
    System.out.println(getName() + " the bear is walking on four legs.");
  }
 
  @Override
  public void swim() {
    System.out.println(getName() + " the bear is swimming in the water.");
  }
 
  @Override
  public void hibernate() {
    System.out.println(getName() + " the bear is hibernating for winter.");
  }
}
Crow.java
public class Crow extends Animal implements Walker, Flyer {
  public Crow(String name, int age) { super(name, age); }
 
  @Override
  public void eat() {
    System.out.println(getName() + " the crow is eating seeds and insects.");
  }
 
  @Override
  public void move() {
    fly();
  }
 
  @Override
  public void sound() {
    System.out.println(getName() + " the crow caws: CAW CAW!");
  }
 
  @Override
  public void walk() {
    System.out.println(getName() + " the crow is hopping on two legs.");
  }
 
  @Override
  public void fly() {
    System.out.println(getName() + " the crow is flying in the sky.");
  }
}
Duck.java
public class Duck extends Animal implements Walker, Flyer, Swimmer, Diver {
  public Duck(String name, int age) { super(name, age); }
 
  @Override
  public void eat() {
    System.out.println(getName() + " the duck is eating seeds and insects.");
  }
 
  @Override
  public void move() {
    fly();
  }
 
  @Override
  public void sound() {
    System.out.println(getName() + " the duck quacks: QUACK!");
  }
 
  @Override
  public void walk() {
    System.out.println(getName() + " the duck is waddling on two legs.");
  }
 
  @Override
  public void fly() {
    System.out.println(getName() + " the duck is flying in the sky.");
  }
 
  @Override
  public void swim() {
    System.out.println(getName() + " the duck is swimming on the water.");
  }
 
  @Override
  public void dive() {
    System.out.println(getName() + " the duck is diving underwater for "
                       + "food.");
  }
}
Gull.java
public class Gull extends Animal implements Walker, Flyer, Swimmer {
  public Gull(String name, int age) { super(name, age); }
 
  @Override
  public void eat() {
    System.out.println(getName() + " the gull is eating fish.");
  }
 
  @Override
  public void move() {
    fly();
  }
 
  @Override
  public void sound() {
    System.out.println(getName() + " the gull squawks: SQUAWK!");
  }
 
  @Override
  public void walk() {
    System.out.println(getName() + " the gull is walking on two legs.");
  }
 
  @Override
  public void fly() {
    System.out.println(getName() + " the gull is flying in the sky.");
  }
 
  @Override
  public void swim() {
    System.out.println(getName() +
                       " the gull is swimming on the water surface.");
  }
}
Lion.java
public class Lion extends Animal implements Walker {
  public Lion(String name, int age) { super(name, age); }
 
  @Override
  public void eat() {
    System.out.println(getName() + " the lion is eating meat.");
  }
 
  @Override
  public void move() {
    walk();
  }
 
  @Override
  public void sound() {
    System.out.println(getName() + " the lion roars: ROAR!");
  }
 
  @Override
  public void walk() {
    System.out.println(getName() + " the lion is walking on four legs.");
  }
}
Penguin.java
public class Penguin extends Animal implements Walker, Swimmer, Diver {
  public Penguin(String name, int age) { super(name, age); }
 
  @Override
  public void eat() {
    System.out.println(getName() + " the penguin is eating fish.");
  }
 
  @Override
  public void move() {
    walk();
  }
 
  @Override
  public void sound() {
    System.out.println(getName() + " the penguin trumpets: HONK!");
  }
 
  @Override
  public void walk() {
    System.out.println(getName() + " the penguin is waddling on two legs.");
  }
 
  @Override
  public void swim() {
    System.out.println(getName() + " the penguin is swimming underwater.");
  }
 
  @Override
  public void dive() {
    System.out.println(getName() + " the penguin is diving deep for fish.");
  }
}

Exercise 3

Dates using LocalDate

  1. Create a class Person and an enumeration Gender using the following code:

    Person.java
    import java.time.LocalDate;
     
    public class Person {
      private String name;
      private LocalDate birthDate;
      private Gender gender;
     
      public Person() {}
      public Person(String name, LocalDate birthDate, Gender gender) {
        this.name = name;
        this.birthDate = birthDate;
        this.gender = gender;
      }
     
      public String getName() { return name; }
      public void setName(String name) { this.name = name; }
     
      public LocalDate getBirthDate() { return birthDate; }
      public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
     
      public Gender getGender() { return gender; }
      public void setGender(Gender gender) { this.gender = gender; }
    }
    Gender.java
    public enum Gender {
      MALE,
      FEMALE;
    }
  2. Create a class PersonTest using the following code:

    PersonTest.java
    import java.time.LocalDate;
     
    public class PersonTest {
      public static void main(String[] args) {
        LocalDate date = LocalDate.of(1994, 9, 15);
        Person p1 = new Person("Jane", date, Gender.FEMALE);
        Person p2 = new Person("John", LocalDate.of(1995, 11, 20), Gender.MALE);
     
        if (p1.getBirthDate().isBefore(p2.getBirthDate()))
          System.out.println(p1.getName() + " is older than " + p2.getName());
        else
          System.out.println(p2.getName() + " is older than " + p1.getName());
      }
    }

Exercise 4

Create a project and implement the following class hierarchy:

PayableInvoiceEmployeeHourlyEmployeeSalariedEmployeeCommissionedEmployee Payable+getPaymentAmount() : double Invoice-partNumber : String-partDescription : String-quantity : int-unitPrice : double+Invoice(String, String, int, double)+getPaymentAmount() : double+toString() : String Employee-firstName : String-lastName : String-qid : String+Employee(String, String, String)+toString() : String HourlyEmployee-wage : double-hours : double+HourlyEmployee(String, String, String, double, double)+getPaymentAmount() : double+toString() : String SalariedEmployee-salary : double+SalariedEmployee(String, String, String, double)+getPaymentAmount() : double+toString() : String CommissionedEmployee-grossSales : double-commissionRate : double+CommissionedEmployee(String, String, String, double, double)+getPaymentAmount() : double+toString() : String

Test your implementation using the following main() method:

PayableTest.java
import java.util.ArrayList;
import java.util.List;
 
public class PayableTest {
  public static void main(String[] args) {
    // create payable list
    List<Payable> payables = new ArrayList<Payable>();
 
    // populate list with objects that implement Payable
    payables.add(new Invoice("01234", "Textbook", 2, 375.00));
    payables.add(new Invoice("56789", "USB Disk", 3, 179.95));
    payables.add(new SalariedEmployee("Ahmed", "Ali", "111-11-1111", 15000.00));
    payables.add(
        new HourlyEmployee("Fatima", "Saleh", "222-22-2222", 160.75, 40));
    payables.add(
        new CommissionedEmployee("Samir", "Sami", "333-33-3333", 100000, .06));
 
    System.out.println("Invoices and Employees processed polymorphically:");
    // generically process each element in array payables
    for (Payable payable : payables) {
      // output payable and its appropriate payment amount
      System.out.printf("ObjectType: %s - PaymentAmount = QR %,.2f\n",
                        payable.getClass().getSimpleName(),
                        payable.getPaymentAmount());
      // if SalariedEmployee then increase the salary by 10%
      if (payable instanceof SalariedEmployee) {
        // downcast Payable reference to
        // SalariedEmployeeEmployee reference
        SalariedEmployee salariedEmployee = (SalariedEmployee)payable;
        double oldBaseSalary = salariedEmployee.getSalary();
        salariedEmployee.setSalary(oldBaseSalary * 1.1);
        System.out.printf("New salary with 10%% increase is: QR %,.2f\n",
                          salariedEmployee.getSalary());
      }
    }
  }
}
Solution
Payable.java
public interface Payable {
  double getPaymentAmount();
}
Invoice.java
public class Invoice implements Payable {
  private String partNumber;
  private String partDescription;
  private int quantity;
  private double unitPrice;
 
  public Invoice(String partNumber, String partDescription, int quantity,
                 double unitPrice) {
    this.partNumber = partNumber;
    this.partDescription = partDescription;
    this.quantity = quantity;
    this.unitPrice = unitPrice;
  }
 
  public String getPartNumber() { return partNumber; }
  public void setPartNumber(String partNumber) { this.partNumber = partNumber; }
 
  public String getPartDescription() { return partDescription; }
  public void setPartDescription(String partDescription) {
    this.partDescription = partDescription;
  }
 
  public int getQuantity() { return quantity; }
  public void setQuantity(int quantity) { this.quantity = quantity; }
 
  public double getUnitPrice() { return unitPrice; }
  public void setUnitPrice(double unitPrice) { this.unitPrice = unitPrice; }
 
  @Override
  public double getPaymentAmount() {
    return quantity * unitPrice;
  }
 
  @Override
  public String toString() {
    return String.format("Invoice [Part #: %s, Description: %s, Quantity: "
                             + "%d, Unit Price: QR %.2f]",
                         partNumber, partDescription, quantity, unitPrice);
  }
}
Employee.java
public abstract class Employee implements Payable {
  private String firstName;
  private String lastName;
  private String qid;
 
  public Employee(String firstName, String lastName, String qid) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.qid = qid;
  }
 
  public String getFirstName() { return firstName; }
  public void setFirstName(String firstName) { this.firstName = firstName; }
 
  public String getLastName() { return lastName; }
  public void setLastName(String lastName) { this.lastName = lastName; }
 
  public String getQid() { return qid; }
  public void setQid(String qid) { this.qid = qid; }
 
  @Override
  public String toString() {
    return String.format("%s %s (QID: %s)", firstName, lastName, qid);
  }
}
HourlyEmployee.java
public class HourlyEmployee extends Employee {
  private double wage;
  private double hours;
 
  public HourlyEmployee(String firstName, String lastName, String qid,
                        double wage, double hours) {
    super(firstName, lastName, qid);
    this.wage = wage;
    this.hours = hours;
  }
 
  public double getWage() { return wage; }
  public void setWage(double wage) { this.wage = wage; }
 
  public double getHours() { return hours; }
  public void setHours(double hours) { this.hours = hours; }
 
  @Override
  public double getPaymentAmount() {
    return wage * hours;
  }
 
  @Override
  public String toString() {
    return String.format("Hourly Employee: %s, Wage: QR %.2f/hr, Hours: %.2f",
                         super.toString(), wage, hours);
  }
}
SalariedEmployee.java
public class SalariedEmployee extends Employee {
  private double salary;
 
  public SalariedEmployee(String firstName, String lastName, String qid,
                          double salary) {
    super(firstName, lastName, qid);
    this.salary = salary;
  }
 
  public double getSalary() { return salary; }
  public void setSalary(double salary) { this.salary = salary; }
 
  @Override
  public double getPaymentAmount() {
    return salary;
  }
 
  @Override
  public String toString() {
    return String.format("Salaried Employee: %s, Salary: QR %.2f",
                         super.toString(), salary);
  }
}
CommissionedEmployee.java
public class CommissionedEmployee extends Employee {
  private double grossSales;
  private double commissionRate;
 
  public CommissionedEmployee(String firstName, String lastName, String qid,
                              double grossSales, double commissionRate) {
    super(firstName, lastName, qid);
    this.grossSales = grossSales;
    this.commissionRate = commissionRate;
  }
 
  public double getGrossSales() { return grossSales; }
  public void setGrossSales(double grossSales) { this.grossSales = grossSales; }
 
  public double getCommissionRate() { return commissionRate; }
  public void setCommissionRate(double commissionRate) {
    this.commissionRate = commissionRate;
  }
 
  @Override
  public double getPaymentAmount() {
    return grossSales * commissionRate;
  }
 
  @Override
  public String toString() {
    return String.format("Commissioned Employee: %s, Gross Sales: QR %.2f, "
                             + "Commission Rate: %.2f%%",
                         super.toString(), grossSales, commissionRate * 100);
  }
}

Exercise 5

Default/Static Interface Methods/Fields

Expert Cleaning Co. provides cleaning services to the residents of The Pearl, Qatar. You are tasked with building an application to allow them to manage the cleaning services provided and the fees to be collected from the residents.

The class diagrams of the application are shown in the figures below:

CleanableVehicleResidenceCarTruckUnitVillaResident Cleanable+fees : Map<String, Integer>+getFee(String) : int+getCleaningCount() : int+setCleaningCount(int) : void+getCleaningFee() : double Resident-id : int-name : String-vehicles : List<Vehicle>-residence : Residence+Resident()+Resident(int, String)+get*() :type+set*(type) : void+addVehicle(Vehicle) : void+getVehicle(String) : Vehicle+deleteVehicle(String) : void+toString() : String Vehicle-plateNumber : String-wheels : int-weight : double-registrationDate : LocalDate-cleaningCount : int+Vehicle()+Vehicle(String, int, double, LocalDate)+get*() :type+set*(type) : void+toString() : String TransmissionType+AUTOMATIC+MANUALCar-passengers : int-transmission : TransmissionType+Car()+Car(String, int, double, LocalDate, int, TransmissionType)+get*() :type+set*(type) : void+toString() : String Truck-load : double+Truck()+Truck(String, int, double, LocalDate, double)+get*() :type+set*(type) : void+getWheelLoad() : double+toString() : String Residence-street : String-roomCount : int-cleaningCount : int+Residence()+Residence(String, int)+get*() :type+set*(type) : void+toString() String Unit-buildingName : String-unitNumber : int+Unit()+Unit(String, int, String, int)+get*() :type+set*(type) : void+toString() : String Villa-floorsCount : int+Villa()+Villa(String, int, int)+get*() :type+set*(type) : void+toString() : String

You are required to modify the Vehicle, Car, Truck, and Resident classes to complete the implementation of the above inheritance hierarchy. Create the TransmissionType as an enumeration and import java.time.LocalDate. The methods’ descriptions are as follow:

  1. Cleanable interface:

    • Use the following Map values for fees: Map.of("Car", 10, "Truck", 15, "Unit", 25, "Villa", 50 );. The getFee(String) returns the fee from the map using the provided string.
    • Why is the method getFee() static?
    • Implement getCleaningFee() as a default method. It returns the product of the cleaning count with the cleaning fee of the class type. Hint: call the getFee(String) method of the interface.
  2. Vehicle class:

    • Implements the Cleanable interface.

    • Modify the parameterized constructor so that it takes four arguments; add plateNumber and registrationDate.

    • Add the setters and getters for the new fields, implementing the interface methods.

    • Modify the toString() method so that it includes plateNumber and registrationDate but only if the plateNumber value is not null, otherwise return "".

  3. Car class:

    • Modify the parameterized constructor to take six arguments; see the class diagram.
  4. Truck class:

    • Modify the parameterized constructor to take five arguments; see the class diagram.
  5. Residence class:

    • Implement the class as shown in the class diagram. This class implements Cleanable.
    • Implement the interface methods in a similar fashion to what you did in class Vehicle.
    • Override the toString() method to return a formatted string representation of the Residence instance.
  6. Unit class:

    • Implement the class as shown in the class diagram. This class extends Residence.
    • Override the toString() method to return a formatted string representation of a Unit instance.
  7. Villa class:

    • Implement the class as shown in the class diagram. This class extends Residence.
    • Override the toString() method to return a formatted string representation of a Villa instance.
  8. Resident class:

    • Copy the Person class from Exercise 8.1 to Resident.

    • Add a residence field and generate its setter and getter.

    • Add a list of vehicles as a private field and create its getter method; a setter is not required.

    • Add the addVehicle() method which adds a Vehicle parameter to the list of vehicles. It should check that the parameter value is not null and that it does not already exist in the list of vehicles.

    • Add the following methods:

      public Vehicle getVehicle(String plateNumber) {
        // searches for a vehicle with plateNumber and returns it or returns null if
        // not found.
      }
       
      public void deleteVehicle(String plateNumber) {
        // searches for a vehicle with plateNumber and deletes it if found.
      }

Create a CleaningCoApp class with a main() method to test the application:

  1. Create a list of residents to store all the residents using the services of Expert Cleaning Co.

  2. Based on the example data provided below, create instances of Resident, and add them to the list of residents. Each resident lives in a Unit or a Villa and has a couple of Vehicles.

    John Doe lives in a Villa @ 55 La Plage Villas, The Pearl, Doha. The villa has 6 rooms and 2 floors. He requests cleaning his villa 4 times a month. He has the following cars:

    Plate NumberWheelsWeightPassengersTransmissionRegistration DateCleaning Count
    120141,9505Automatic2022-03-225

    He owns the following trucks:

    Plate NumberWheelsWeightLoadRegistration DateCleaning Count
    4211124,00012,0002023-07-015
    8003103,0008,0002015-08-241
    8025124,50015,0002019-02-212

    Jane Doe lives in a Unit @ 508 West Porto Drive, The Pearl, Doha. The Unit number is 16 at Porto Arabia building. The unit has 2 rooms. She requests cleaning her unit 3 times a month. She has the following cars:

    Plate NumberWheelsWeightPassengersTransmissionRegistration DateCleaning Count
    490741,9505Automatic2022-03-185
    909343,0008Manual2018-12-011
  3. Loop through the list of residents and display for each resident:

    • Resident name and address,
    • residence type,
    • the number of vehicles, and
    • the cleaning fees to be paid.
Solution
Cleanable.java
import java.util.Map;
 
public interface Cleanable {
  static Map<String, Integer> fees =
      Map.of("Car", 10, "Truck", 15, "Unit", 25, "Villa", 50);
 
  static int getFee(String type) { return fees.getOrDefault(type, 0); }
 
  int getCleaningCount();
  void setCleaningCount(int count);
 
  default double getCleaningAmount() {
    return getCleaningCount() * getFee(getClass().getSimpleName());
  }
}
Vehicle.java
import java.time.LocalDate;
 
public abstract class Vehicle implements Cleanable {
  private String plateNumber;
  private int wheels;
  private double weight;
  private LocalDate registrationDate;
  private int cleaningCount;
 
  public Vehicle() {}
  public Vehicle(String plateNumber, int wheels, double weight,
                 LocalDate registrationDate) {
    this.plateNumber = plateNumber;
    this.wheels = wheels;
    this.weight = weight;
    this.registrationDate = registrationDate;
  }
 
  public String getPlateNumber() { return plateNumber; }
  public void setPlateNumber(String plateNumber) {
    this.plateNumber = plateNumber;
  }
 
  public int getWheels() { return wheels; }
  public void setWheels(int wheels) { this.wheels = wheels; }
 
  public double getWeight() { return weight; }
  public void setWeight(double weight) { this.weight = weight; }
 
  public LocalDate getRegistrationDate() { return registrationDate; }
  public void setRegistrationDate(LocalDate registrationDate) {
    this.registrationDate = registrationDate;
  }
 
  @Override
  public int getCleaningCount() {
    return cleaningCount;
  }
 
  @Override
  public void setCleaningCount(int cleaningCount) {
    this.cleaningCount = cleaningCount;
  }
 
  @Override
  public String toString() {
    if (plateNumber != null) {
      return String.format("Plate #: %s, Registration Date: %s", plateNumber,
                           registrationDate);
    }
    return "";
  }
}
TransmissionType.java
public enum TransmissionType {
  AUTOMATIC,
  MANUAL;
}
Car.java
import java.time.LocalDate;
 
public class Car extends Vehicle {
  private int passengers;
  private TransmissionType transmission;
 
  public Car() {}
  public Car(String plateNumber, int wheels, double weight,
             LocalDate registrationDate, int passengers,
             TransmissionType transmission) {
    super(plateNumber, wheels, weight, registrationDate);
    this.passengers = passengers;
    this.transmission = transmission;
  }
 
  public int getPassengers() { return passengers; }
 
  public void setPassengers(int passengers) { this.passengers = passengers; }
 
  public TransmissionType getTransmission() { return transmission; }
 
  public void setTransmission(TransmissionType transmission) {
    this.transmission = transmission;
  }
 
  @Override
  public String toString() {
    return String.format("Car [%s, Wheels: %d, Weight: %.0f kg, Passengers: "
                             + "%d, Transmission: %s, Cleaning Count: %d]",
                         super.toString(), getWheels(), getWeight(), passengers,
                         transmission, getCleaningCount());
  }
}
Truck.java
import java.time.LocalDate;
 
public class Truck extends Vehicle {
  private double load;
 
  public Truck() {}
 
  public Truck(String plateNumber, int wheels, double weight,
               LocalDate registrationDate, double load) {
    super(plateNumber, wheels, weight, registrationDate);
    this.load = load;
  }
 
  public double getLoad() { return load; }
  public void setLoad(double load) { this.load = load; }
 
  public double getWheelLoad() { return (getWeight() + load) / getWheels(); }
 
  @Override
  public String toString() {
    return String.format("Truck [%s, Wheels: %d, Weight: %.0f kg, Load: %.0f "
                             + "kg, Wheel Load: %.2f kg, Cleaning Count: %d]",
                         super.toString(), getWheels(), getWeight(), load,
                         getWheelLoad(), getCleaningCount());
  }
}
Residence.java
public abstract class Residence implements Cleanable {
  private String street;
  private int roomCount;
  private int cleaningCount;
 
  public Residence() {}
 
  public Residence(String street, int roomCount) {
    this.street = street;
    this.roomCount = roomCount;
  }
 
  public String getStreet() { return street; }
  public void setStreet(String street) { this.street = street; }
 
  public int getRoomCount() { return roomCount; }
  public void setRoomCount(int roomCount) { this.roomCount = roomCount; }
 
  @Override
  public int getCleaningCount() {
    return cleaningCount;
  }
 
  @Override
  public void setCleaningCount(int cleaningCount) {
    this.cleaningCount = cleaningCount;
  }
 
  @Override
  public String toString() {
    return String.format("Street: %s, Rooms: %d, Cleaning Count: %d", street,
                         roomCount, cleaningCount);
  }
}
Unit.java
public class Unit extends Residence {
  private String buildingName;
  private int unitNumber;
 
  public Unit() {}
  public Unit(String street, int roomCount, String buildingName,
              int unitNumber) {
    super(street, roomCount);
    this.buildingName = buildingName;
    this.unitNumber = unitNumber;
  }
 
  public String getBuildingName() { return buildingName; }
  public void setBuildingName(String buildingName) {
    this.buildingName = buildingName;
  }
 
  public int getUnitNumber() { return unitNumber; }
  public void setUnitNumber(int unitNumber) { this.unitNumber = unitNumber; }
 
  @Override
  public String toString() {
    return String.format("Unit [Building: %s, Unit #: %d, %s]", buildingName,
                         unitNumber, super.toString());
  }
}
Villa.java
public class Villa extends Residence {
  private int floorsCount;
 
  public Villa() {}
  public Villa(String street, int roomCount, int floorsCount) {
    super(street, roomCount);
    this.floorsCount = floorsCount;
  }
 
  public int getFloorsCount() { return floorsCount; }
  public void setFloorsCount(int floorsCount) {
    this.floorsCount = floorsCount;
  }
 
  @Override
  public String toString() {
    return String.format("Villa [Floors: %d, %s]", floorsCount,
                         super.toString());
  }
}
Resident.java
import java.util.ArrayList;
import java.util.List;
 
public class Resident {
  private int id;
  private String name;
  private List<Vehicle> vehicles;
  private Residence residence;
 
  public Resident() { this.vehicles = new ArrayList<>(); }
 
  public Resident(int id, String name) {
    this.id = id;
    this.name = name;
    this.vehicles = new ArrayList<>();
  }
 
  public int getId() { return id; }
  public void setId(int id) { this.id = id; }
 
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
 
  public Residence getResidence() { return residence; }
  public void setResidence(Residence residence) { this.residence = residence; }
 
  public List<Vehicle> getVehicles() { return vehicles; }
 
  public void addVehicle(Vehicle vehicle) {
    if (vehicle != null && !vehicles.contains(vehicle)) {
      vehicles.add(vehicle);
    }
  }
 
  public Vehicle getVehicle(String plateNumber) {
    for (Vehicle vehicle : vehicles) {
      if (vehicle.getPlateNumber().equals(plateNumber)) {
        return vehicle;
      }
    }
    return null;
  }
 
  public void deleteVehicle(String plateNumber) {
    Vehicle vehicle = getVehicle(plateNumber);
    if (vehicle != null) {
      vehicles.remove(vehicle);
    }
  }
 
  @Override
  public String toString() {
    return String.format("Resident [ID: %d, Name: %s]", id, name);
  }
}
CleaningCoApp.java
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
 
public class CleaningCoApp {
  public static void main(String[] args) {
    List<Resident> residents = new ArrayList<>();
 
    Resident johnDoe = new Resident(1, "John Doe");
    Villa johnVilla = new Villa("55 La Plage Villas, The Pearl, Doha", 6, 2);
    johnVilla.setCleaningCount(4);
    johnDoe.setResidence(johnVilla);
 
    Car johnCar = new Car("1201", 4, 1950, LocalDate.of(2022, 3, 22), 5,
                          TransmissionType.AUTOMATIC);
    johnCar.setCleaningCount(5);
    johnDoe.addVehicle(johnCar);
 
    Truck truck1 = new Truck("4211", 12, 4000, LocalDate.of(2023, 7, 1), 12000);
    truck1.setCleaningCount(5);
    johnDoe.addVehicle(truck1);
 
    Truck truck2 = new Truck("8003", 10, 3000, LocalDate.of(2015, 8, 24), 8000);
    truck2.setCleaningCount(1);
    johnDoe.addVehicle(truck2);
 
    Truck truck3 =
        new Truck("8025", 12, 4500, LocalDate.of(2019, 2, 21), 15000);
    truck3.setCleaningCount(2);
    johnDoe.addVehicle(truck3);
 
    residents.add(johnDoe);
 
    Resident janeDoe = new Resident(2, "Jane Doe");
    Unit janeUnit = new Unit("508 West Porto Drive, The Pearl, Doha", 2,
                             "Porto Arabia", 16);
    janeUnit.setCleaningCount(3);
    janeDoe.setResidence(janeUnit);
 
    Car janeCar1 = new Car("4907", 4, 1950, LocalDate.of(2022, 3, 18), 5,
                           TransmissionType.AUTOMATIC);
    janeCar1.setCleaningCount(5);
    janeDoe.addVehicle(janeCar1);
 
    Car janeCar2 = new Car("9093", 4, 3000, LocalDate.of(2018, 12, 1), 8,
                           TransmissionType.MANUAL);
    janeCar2.setCleaningCount(1);
    janeDoe.addVehicle(janeCar2);
 
    residents.add(janeDoe);
 
    System.out.println("Expert Cleaning Co. - Resident Summary");
    System.out.println("=".repeat(80));
    System.out.println();
 
    for (Resident resident : residents) {
      System.out.println("Resident: " + resident.getName());
      System.out.println("Address: " + resident.getResidence().getStreet());
      System.out.println("Residence Type: " +
                         resident.getResidence().getClass().getSimpleName());
      System.out.println("Number of Vehicles: " +
                         resident.getVehicles().size());
 
      double totalFees = 0;
 
      if (resident.getResidence() != null) {
        double residenceFee = resident.getResidence().getCleaningAmount();
        totalFees += residenceFee;
        System.out.printf("  - Residence Cleaning Fee: QR %.2f\n",
                          residenceFee);
      }
 
      for (Vehicle vehicle : resident.getVehicles()) {
        double vehicleFee = vehicle.getCleaningAmount();
        totalFees += vehicleFee;
        System.out.printf("  - %s (Plate: %s) Cleaning Fee: QR %.2f\n",
                          vehicle.getClass().getSimpleName(),
                          vehicle.getPlateNumber(), vehicleFee);
      }
 
      System.out.printf("Total Cleaning Fees: QR %.2f\n", totalFees);
      System.out.println("-".repeat(80));
      System.out.println();
    }
  }
}

Exercise 6+

Vehicle Hierarchy

Consider a hierarchy of vehicles with a base class Vehicle and derived classes like Car, Bicycle, and Boat. Each vehicle can have a move() method, and each derived class can implement it differently, simulating how different types of vehicles move.

Game Characters

Design a simple video game with characters like warriors, mages, and archers. Create a base class Character and subclasses for each character type. Implement polymorphic methods like attack(), defend(), and specialAbility() for each character.

Library System

Design a library management system with a base class LibraryItem and derived classes like Book, DVD, and Magazine. Each item type can have its own implementation of methods like checkOut() and returnItem(). Use polymorphism to handle different library items within the same system.

Vehicle Fleet

Create a program to manage a fleet of vehicles, including cars, trucks, and motorcycles. Define a base class Vehicle and subclasses for each vehicle type. Use polymorphism to handle common operations like start(), stop(), and fuelUp(). How would update your class hierarchy to account for electric vehicles?

Banking Transactions

Create a banking system with a base class Account and derived classes like SavingsAccount, CheckingAccount, and LoanAccount. Each account type can implement methods for depositing, withdrawing, and checking balances.

Payment Methods

Create a payment processing system with a base class PaymentMethod and derived classes like CreditCard, PayPal, and BankTransfer. Each payment method can have its own implementation for processing payments.

Online Shopping Cart

Develop an online shopping cart system with a base class Product and derived classes for product categories like Electronics, Clothing, and Books. Each product type can have its own pricing and shipping logic, making use of polymorphism to manage the cart.