【问题标题】:how to recognize the types of objects inside an array of objects?如何识别对象数组中的对象类型?
【发布时间】:2013-06-03 12:35:41
【问题描述】:

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

【问题讨论】:

  • 您已经有一个使用 Area() 方法的示例(顺便说一下,您应该将其重命名为 area(),以遵守 Java 中的命名约定)。
  • 就像 fge 说的......如果你只想打印它们的属性,你需要覆盖 toString() 并打印出值
  • @fge 是的;更好的是getAreacalculateArea。据我所知,area不是动词。
  • 使用 instanceof 运算符

标签: java oop


【解决方案1】:

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

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

例如

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

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

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

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

【讨论】:

  • 鉴于提问者的情况,他还需要在instanceof检查后将类型转换为正确的类型以使代码编译。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    一般来说,当您迭代异构集合的项目时(在本例中,包含Rectangles 和Circles),您有两种选择:

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

    设计A:把所有的功能都放在Shape

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

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

    您也可以覆盖Object 上的toString() 方法,但通常最好只使用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)
      })
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-24
      • 2013-09-24
      • 2012-05-18
      • 2013-02-09
      • 1970-01-01
      • 1970-01-01
      • 2011-09-13
      • 2013-04-20
      相关资源
      最近更新 更多