Search code examples
javaoopexceptioncastingdecorator

ClassCastException while following Decorator pattern


I'm learning to use Decorator pattern in some combination with Template method in Java. I have a compilation error

Exception in thread "main" java.lang.ClassCastException: class Room cannot be cast to class Flat (Room and Flat are in unnamed module of loader 'app')
    at Main.main(Main.java:11)

but I have no idea why it happens and how to correct it. In a nutshell, my program has to calculate area of a flat adding areas of its rooms and customers can add flats to their basket. So I've created an abstract class Premise for Flats and Rooms as well.

public abstract class Premise {
    protected double dwellingArea;
    protected double totalArea;
    public String getInfo(){ return "Rooms: "; }
    public abstract double getDwellingArea();
    public abstract double getTotalArea();
}

I also have an abstract class Decorator.

public abstract class Decorator extends Premise {
    public abstract String getInfo();
    Decorator(double totalArea){
        this.totalArea=totalArea;
    }
}

After that I created class Flat that extends class Premise.

public class Flat extends Premise{
    private int floor;
    private double price;
    private String description;
    public int getFloor() {
        return floor;
    }
    public void setFloor(int floor) {
        this.floor = floor;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public Flat(int floor, double price, String description) {
        super();
        this.floor = floor;
        this.price = price;
        this.description=description;
    }
    @Override
    public double getDwellingArea() {
        return dwellingArea;
    }
    @Override
    public double getTotalArea() {
        return totalArea;
    }
}

Class Room extends Decorator.

public class Room extends Decorator{
    boolean isService;
    Premise apartment;

    @Override
    public String getInfo() {
        if(isService) {
            return apartment.getInfo()+" "+"service room ";
        }
        else {
            return apartment.getInfo()+" "+"dwelling room ";
        }
    }

    @Override
    public double getDwellingArea() {
        return apartment.dwellingArea+this.dwellingArea;
    }

    @Override
    public double getTotalArea() {
        return apartment.totalArea+this.totalArea;
    }

    Room(double totalArea, boolean isService, Premise apartment) {
        super(totalArea);
        this.isService=isService;
        if(isService=true) {
            dwellingArea=0;
        }
        else {
            dwellingArea=totalArea;
        }
        this.apartment=apartment;
    }
}

Also I have an abstract class User for both customers and realtors.

public abstract class User {
    protected String name;
    protected String phone;
    protected String email;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public User(String name, String phone, String email) {
        this.name = name;
        this.phone = phone;
        this.email = email;
    }
    public abstract void showList();
}

In class Customer there is an array list of selected flats.

import java.util.ArrayList;
public class Customer extends User{
    private ArrayList<Flat> flats;
    public Customer(String name, String phone, String email) {
        super(name, phone, email);
        flats=new ArrayList<Flat>();
    }
    @Override
    public void showList() {
        System.out.println("Customer "+name+" selected "+flats.size()+" apartments:");
        for(Flat flat:flats) {
            System.out.println(flat.getDescription());
        }
        
    }
    public void addFlat(Flat flat) {
        flats.add(flat);
    }
}

In class Main I'm trying to create flat and its room and a customer and later add the flat to the customer's list but it gives me the mistake that I mentioned above.


public class Main {

    public static void main(String[] args) {
        Premise flat1 = new Flat(5,25000,"A flat in a big city");
        flat1 = new Room(15, true, (Flat)flat1);
        Flat flat2 = new Flat(10,45000,"A flat in a large city"); 
        Customer client1 = new Customer("Jane Peterson","0950297789","jane@gmail.com"); 
        client1.addFlat((Flat)flat1); 
        client1.addFlat(flat2); 
    }

}

I will be extremely grateful if you told me why it goes like this and how to tackle the issue.


Solution

  • A Room is a Decorator is a Premise. But it's not a Flat. (The Room you create in line 8 has a Flat as it's Premise apartment member, but that doesn't make it a Flat!)

    As for fixing this... You'd have to find a way to move the getDescription() method to the Premise class. That way, your Customer could have a List<Premise> instead of a ArrayList<Flat>. Replace the addFlat(Flat flat) method with addPremise(Premise premise) and remove the cast to Flat when you call the new method in main().

    btw: Why do you think the Decorator Pattern is a good way to model this? What does a Room decorating another Premise even mean? Isn't a Room (if rented separately) just another Premise?