You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java装饰器模式结合模板方法时出现ClassCastException问题求助

问题:Java装饰器模式使用中的ClassCastException异常

我正在学习在Java中结合使用装饰器模式与模板方法,编写了一个计算公寓总面积(累加房间面积)并支持将公寓加入购物篮的程序,但运行时抛出了如下异常:

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)

以下是我的代码实现:

抽象类Premise

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

抽象装饰器类Decorator

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

Flat类

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;
    }
}

Room类

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;
    }
}

User抽象类

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();
}

Customer类

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);
    }
}

Main类

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); 
    }

}

异常原因分析

  1. 类型转换错误:在Main类第11行client1.addFlat((Flat)flat1);,此时flat1的实际类型是Room,而非Flat。因为之前执行了flat1 = new Room(15, true, (Flat)flat1);,将flat1的引用替换为了Room对象。Room和Flat都是Premise的子类,属于兄弟类关系,无法互相强制转换,因此抛出ClassCastException。
  2. 装饰器模式与购物篮设计冲突:Customer的addFlat方法仅接受Flat类型参数,但装饰器模式下,给Flat添加Room后得到的是Room对象(属于Premise类型),不再是Flat,两者类型不兼容。
  3. Room类的潜在bug:构造方法中if(isService=true)是赋值操作,会导致所有Room都被判定为服务房间,应该改为if(isService == true)或直接if(isService)。

解决方法

方案1:调整购物篮支持所有Premise类型

修改Customer类,将购物篮的泛型从Flat改为Premise,同时适配相关方法:

import java.util.ArrayList;
public class Customer extends User{
    private ArrayList<Premise> premises;
    public Customer(String name, String phone, String email) {
        super(name, phone, email);
        premises=new ArrayList<Premise>();
    }
    @Override
    public void showList() {
        System.out.println("Customer "+name+" selected "+premises.size()+" premises:");
        for(Premise premise:premises) {
            // 如果需要获取Flat的专属属性,可做类型判断
            if(premise instanceof Flat) {
                System.out.println(((Flat)premise).getDescription());
            }
            // 同时可以输出面积信息
            System.out.println("Total area: " + premise.getTotalArea());
            System.out.println("Dwelling area: " + premise.getDwellingArea());
            System.out.println("Info: " + premise.getInfo());
        }
    }
    public void addPremise(Premise premise) {
        premises.add(premise);
    }
}

修改Main类,直接传入Premise对象,无需强制转换:

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, flat1); // 无需强制转换为Flat
        Premise flat2 = new Flat(10,45000,"A flat in a large city"); 
        Customer client1 = new Customer("Jane Peterson","0950297789","jane@gmail.com"); 
        client1.addPremise(flat1); 
        client1.addPremise(flat2);
        client1.showList();
    }
}

方案2:修正装饰器设计,保留Flat引用(可选)

如果需要严格区分Flat和装饰后的Flat,可以让Room装饰器仅接受Flat作为参数,并添加获取原始Flat的方法:

public class Room extends Decorator{
    boolean isService;
    Flat apartment; // 改为Flat类型

    // ... 其他方法不变 ...

    Room(double totalArea, boolean isService, Flat apartment) {
        super(totalArea);
        this.isService=isService;
        if(isService) { // 修正bug
            dwellingArea=0;
        }
        else {
            dwellingArea=totalArea;
        }
        this.apartment=apartment;
    }

    // 添加获取原始Flat的方法
    public Flat getOriginalFlat() {
        return apartment;
    }
}

此时在Main类中可以通过getOriginalFlat()获取Flat对象加入购物篮,但这种方式会破坏装饰器模式的透明性,不推荐优先使用。

必做:修复Room构造方法的bug

将if(isService=true)改为if(isService),避免错误赋值:

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

内容的提问来源于stack exchange,提问作者SaintSammy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 15:25:05