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); } }
异常原因分析
- 类型转换错误:在Main类第11行
client1.addFlat((Flat)flat1);,此时flat1的实际类型是Room,而非Flat。因为之前执行了flat1 = new Room(15, true, (Flat)flat1);,将flat1的引用替换为了Room对象。Room和Flat都是Premise的子类,属于兄弟类关系,无法互相强制转换,因此抛出ClassCastException。 - 装饰器模式与购物篮设计冲突:
Customer的addFlat方法仅接受Flat类型参数,但装饰器模式下,给Flat添加Room后得到的是Room对象(属于Premise类型),不再是Flat,两者类型不兼容。 - 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
相关产品推荐
相关产品推荐

