如何识别对象数组中的对象类型?

时间:2013-06-02 03:54:49

标签: java oop

我有一个名为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个位置的对象是一个Rectangle或Circle来打印它的属性,请记住我只有一个Shapes数组。我想我可以用接口来做,但是如何只使用抽象类来做。那可能吗? 感谢

3 个答案:

答案 0 :(得分:2)

  

我是指如何识别第i个位置的对象是矩形还是圆形......

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

e.g。

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

这不是一个好习惯(这些都是你自己的类,但你仍然需要检查对象的类型(在运行时)并使用显式转换)。

但如果你想要做的就是打印属性,那么你可以让两个子类正确地覆盖toString(),然后就可以了 -

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

答案 1 :(得分:0)

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

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

您是否考虑让Shape类声明一个getAttributes方法,该方法可以返回一个HashMap,其属性名称作为访问相应值的键?圆圈将具有“半径”键,矩形将具有“基本”和“高度”键,并且两者都具有“区域”和“颜色”键。

答案 2 :(得分:0)

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

  1. 仅处理父类型上可用的方法(此处为Shape)。
  2. 使用instanceof检查以不同方式处理每个子类型。
  3. 设计A:将所有功能放在Shape

    通过这种设计,每个Shape都有助于了解如何打印自己的属性。这使用了选择1 - 循环代码永远不需要知道它是Rectangle还是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()上的Object方法,但通常最好只使用toString进行调试,而不是例如需要显示的内容一个用户。类通常应该有toString覆盖,用于打印实例数据的完整表示。

    设计B:支票实例

    这是选择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)
      })
    }