向ArrayList<Shape>添加子类对象报错的技术求助
问题根源
最核心的问题是**Shape.Rectangle没有继承Shape类**。虽然你把它定义成了Shape的静态内部类,但这只是代码位置上的嵌套,没有继承关系的话,编译器不认它是Shape类型,自然塞不进ArrayList<Shape>里。
另外,改成ArrayList<Object>后,从列表里取出来的是Object对象,而Object本身没有getName()和area()方法,调用时必然报错。
修复步骤
1. 让图形子类继承Shape抽象类
所有图形子类(Rectangle、Circle、Triangle)都要继承Shape,这样它们才是Shape的子类,能被添加到ArrayList<Shape>中。而且Shape已经定义了name属性和对应的get/set方法,子类不用重复编写,直接复用即可,只需要实现抽象的area()方法,加上自己独有的属性(比如矩形的长和宽)。
修正后的Rectangle类:
/** * Rectangle子类 */ public static class Rectangle extends Shape { /** * 矩形独有的实例变量:长、宽 */ private Double length, width; /** * 默认构造方法 */ public Rectangle() { super("Rectangle"); // 调用父类构造方法设置名称 this.length = 1.0; this.width = 1.0; } /** * 带参构造方法 */ public Rectangle(String name, Double length, Double width) { super(name); // 调用父类构造方法设置名称 this.length = length; this.width = width; } // 只保留矩形独有的get/set方法 public Double getLength() { return length; } public void setLength(Double newLength) { this.length = newLength; } public Double getWidth() { return width; } public void setWidth(Double newWidth) { this.width = newWidth; } /** * 实现父类的抽象area方法 */ @Override public double area() { return this.length * this.width; } /** * 修正equals方法,正确比较矩形对象 */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Rectangle rectangle = (Rectangle) obj; // 复用父类的name属性进行比较 return getName().equals(rectangle.getName()) && length.equals(rectangle.length) && width.equals(rectangle.width); } /** * 优化toString方法 */ @Override public String toString() { return super.toString() + ", 长: " + length + ", 宽: " + width; } }
Circle和Triangle类也要照这个逻辑修改:继承Shape,实现area方法,去掉重复的name相关代码。
2. 修正Shape类的equals方法
原Shape的equals方法逻辑完全错误——它把传入的对象和字符串(this.name)直接用==比较,这永远不会成立。正确的equals应该先判断对象是否是同一实例,再判断类型,最后比较属性:
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Shape shape = (Shape) obj; return name.equals(shape.name); }
3. 修复Scanner读取的换行问题
原create方法里,用scan.nextLine()读取shapeType后,调用scan.nextDouble()只会读取数字,不会消耗后面的换行符,导致下一次scan.nextLine()读到空字符串,逻辑混乱。所以每次读取完double后,要加一行scan.nextLine()来消耗换行符:
修正后的create方法:
public static void create(ArrayList<Shape> list) throws FileNotFoundException { File dataFile = new File("./src/shape/dataFile.txt"); Scanner scan = new Scanner(dataFile); while (scan.hasNextLine()) { String shapeType = scan.nextLine().trim(); if (shapeType.isEmpty()) continue; // 跳过空行 if (shapeType.equals("Rectangle")) { String name = scan.nextLine().trim(); Double length = scan.nextDouble(); Double width = scan.nextDouble(); scan.nextLine(); // 消耗nextDouble后的换行符 list.add(new Shape.Rectangle(name, length, width)); } else if (shapeType.equals("Circle")) { String name = scan.nextLine().trim(); Double radius = scan.nextDouble(); scan.nextLine(); // 消耗换行符 list.add(new Shape.Circle(name, radius)); } else if (shapeType.equals("Triangle")) { String name = scan.nextLine().trim(); Double side1 = scan.nextDouble(); Double side2 = scan.nextDouble(); Double side3 = scan.nextDouble(); scan.nextLine(); // 消耗换行符 list.add(new Shape.Triangle(name, side1, side2, side3)); } } scan.close(); }
总结
做完这些修改后,ArrayList<Shape>就能正常添加各个图形子类对象了,display方法也能正确调用父类的getName()和子类实现的area()方法。另外记得Circle和Triangle类也要做和Rectangle一样的继承修正,不然还是会报错。
内容的提问来源于stack exchange,提问作者OhmsLaw

