0

我有一个名为 Shape 的超类:

abstract class Shape {
    private String color;
    public Shape(String color) {
        this.color=color;
    }
    public String getColor(){return color;}
    abstract double Area();
}

和两个继承类:

class Rectangle extends Shape {
   private double b,h;
   public Rectangle(double b,double h, String color) {
        super(color);
        this.b=b;
        this.h=h;         
   }
   public double getB() { return b;}
   public double getH() { return h;}
   public double Area() {
        return b*h;
   }

}

class Circle extends Shape {
   private double r;
   public Circle(double r,String color) {
        super(color);
        this.r=r;         
   }
   public double getR() {
      return r;
   }
   public double Area() {
        return 3.14*r*r;
   }
}

现在我创建了一组对象或形状(矩形和圆形)。我遇到的问题是当我想遍历这个数组的元素并打印它们的属性时。我想做这样的事情:

for (int i=0;i<lengthShapes;i++) {
    System.out.println(shapes[i].getB()+shapes[i].getH()+shapes[i].getR());
}

我的意思是如何识别第 i 个位置的对象是用于打印其属性的矩形或圆形,请记住,我只有一个形状数组。我想我可以用接口来做到这一点,但如何只使用抽象类来做到这一点。那可能吗?谢谢

4

3 回答 3

2

我的意思是如何识别第 i 个位置的对象是矩形或圆形...

最简单的方法是使用instanceof运算符。

例如

if(shapes[i] instanceof Rectangle) {
    Rectangle rect = (Rectangle) shapes[i];
    // ...
}

这不是一个好的做法(这些都是您自己的类,但您仍然必须检查对象的类型(在运行时)并使用显式转换)。

但是,如果您只想打印属性,那么您可以让两个子类都toString()正确覆盖,然后您就可以这样做 -

System.out.println(shapes[i]);
于 2013-06-02T04:03:13.157 回答
0

您可以从 class 获取类标识符信息Class。例如,要获取当前类的名称,String您可以使用以下方法getCanonicalName

System.out.println(shapes[i].getClass().getCanonicalName());

您是否考虑过让您的Shape类声明一个getAttributes方法,该方法可以返回一个带有属性名称的 HashMap 作为访问相应值的键?圆形有“radius”键,矩形有“base”和“height”键,两者都有“area”和“color”键。

于 2013-06-02T19:34:11.873 回答
0

通常,当您迭代异构集合的项目时(在这种情况下,包含Rectangles 和Circles),您有两种选择:

  1. 仅处理父类型(此处为Shape)上可用的方法。
  2. 使用instanceof检查以不同方式处理每个子类型。

设计A:将所有功能放入Shape

通过这种设计,每个Shape人都有责任知道如何打印自己的属性。这使用选项 1 - 循环代码永远不需要知道它是否具有 aRectangle或 a Circle

,你Shape会添加

    abstract String getAttributesString();

Rectangle将此实现为

    @Override
    String getAttributesString() {
        return String.format("Rectangle {b=%f, h=%f}", b, h);
    }

然后循环是

for (Shape shape : shapes) {
    System.out.println(shape.getAttributesString());
}

您也可以覆盖toString()on 方法Object,但通常最好只toString用于调试,而不是用于例如需要向用户显示的内容。类通常应该具有toString打印实例数据的完整表示的覆盖。

设计 B:Instanceof 检查

这是选择 2。更改仅针对循环代码 - 它根本不需要修改形状。

    for (Shape shape : shapes) {
        if (shape instanceof Rectangle) {
            Rectangle rectangle = (Rectangle) shape;
            System.out.println(String.format("Rectangle {b=%f, h=%f}", rectangle.getB(), rectangle.getH()));
        } else if (shape instanceof Circle) {
            Circle circle = (Circle) shape;
            System.out.println(String.format("Circle {r=%f}", circle.getR()));
        }
    }

设计C:访客模式

这是两种选择的混合。

interface ShapeVisitor {
    void visitRectangle(Rectangle rectangle);
    void visitCircle(Circle circle);
}

abstract class Shape {

    void visit(ShapeVisitor visitor);

    /* ... the rest of your code ... */
}

class Rectangle extends Shape {

    @Override
    void visit(ShapeVisitor visitor) {
        visitor.visitRectangle(this);
    }

    /* ... the rest of your code ... */
}

class Circle extends Shape {

    @Override
    void visit(ShapeVisitor visitor) {
        visitor.visitCircle(this);
    }

    /* ... the rest of your code ... */
}

然后循环看起来像:

for (Shape shape : shapes) {
    shape.visit(new ShapeVisitor() {
        @Override
        void visitRectangle(Rectangle rectangle) {
            System.out.println(String.format("Rectangle {b=%f, h=%f}", rectangle.getB(), rectangle.getH()));
        }
        @Override
        void visitCircle(Circle circle) {
            System.out.println(String.format("Circle {r=%f}", circle.getR()));
        }
    });
}

使用哪个?

设计 A 很好,因为它避免instanceof和铸造,但缺点是您必须将打印逻辑放在形状类本身中,这会失去一些灵活性(如果您想在两种不同的情况下以不同的方式打印相同的形状列表怎么办? ?)。

设计 B 将打印逻辑放在您想要的位置,但是通过强制转换您无法充分利用编译器的类型检查。这很容易出错,因为例如,如果您要添加另一个Shape子类型,您可能会忘记更新循环代码。

设计 C 结合了 A 和 B 的最佳特性。但问题是它不可扩展。如果您正在编写一个其他人将用来定义他们自己的Shape子类型的库,他们就不能这样做,因为这需要修改ShapeVisitor接口。但如果您的代码不需要以这种方式扩展,则它是合适的。

或者...

最终,所有这些在 Scala 中都变得更容易了。(是的,我意识到我不再回答你的问题,只是现在玩得开心。)

sealed trait Shape {
  def color: String
  def area: Double
}

case class Rectangle(b: Double, h: Double, color: String) extends Shape {
  def area: Double = b*h
}

case class Circle(r: Double, color: String) extends Shape {
  def area: Double = math.Pi*r*r
}

for (shape <- shapes) {
  println(shape match {
    case rectangle: Rectangle =>
      "Rectangle {b=%f, h=%f}".format(rectangle.b, rectangle.h)
    case circle: Circle =>
      "Circle {r=%f}".format(circle.r)
  })
}
于 2013-06-03T04:26:08.840 回答