Little
Little

Reputation: 3477

how to recognize the types of objects inside an array of objects?

I have a superclass called Shape:

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

and two inheriting classes:

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

Now I have created an array of objects or Shapes (rectangles and circles). The problem that I have is when I want to iterate over the elements of this array and print their atributes. I want to do something like this:

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

I mean how I can do to recognize that the object in the ith position is a Rectangle or a Circle for printing its atributes, keep in mind that I only have an array of Shapes. I think I can do it with interfaces, but how to do it only using abstract classes. Is that possible? Thanks

Upvotes: 0

Views: 289

Answers (3)

Chris Martin
Chris Martin

Reputation: 30736

In general, when you're iterating over the items of a heterogeneous collection (in this case, containing both Rectangles and Circles), you have two choices:

  1. Only deal with methods available on the parent type (here, Shape).
  2. Use instanceof checks to handle each subtype differently.

Design A: Put all the functionality in Shape

With this design, every Shape is reponsible for knowing how to print its own attributes. This uses choice 1 - the looping code never needs to know whether it has a Rectangle or a Circle.

To Shape, you'd add

    abstract String getAttributesString();

Rectangle implements this as

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

Then the loop is

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

You could also override the toString() method on Object, but it's usually best to only use toString for debugging, rather than for something that, for example, needs to be shown to a user. Classes should generally have a toString override that prints a full representation of the instance's data.

Design B: Instanceof checks

This is choice 2. The change is only to the loop code - It does not require modifying the shapes at all.

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

Design C: Visitor pattern

This is sort of a mix of the two choices.

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 ... */
}

Then the loop looks like:

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

Which to use?

Design A was nice because it avoids instanceof and casting, but the drawback was that you had to put the printing logic in the shape classes themselves, which takes away some flexibility (what if you wanted to print the same list of shapes differently in two different situations?).

Design B put the printing logic where you wanted it, but with casting you don't get to take full advantage of the compiler's type checking. It's error-prone because if you were to, for instance, add another Shape subtype, you could forget to update the loop code.

Design C sort of combines the best features of A and B. The problem, though, is that it isn't extensible. If you're writing a library that other people are going to use to define their own Shape subtypes, they can't do that because it would require modifying the ShapeVisitor interface. But it is suitable if your code doesn't need to be extensible in that way.

Or...

Ultimately, all of this is way easier in Scala. (yes, I realize I am no longer answering your question, and just having fun now.)

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

Upvotes: 0

pjs
pjs

Reputation: 19855

You can obtain the class identifier info from class Class. For instance, to get the current class's name as a String you can use the method getCanonicalName:

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

Have you considered having your Shape class declare a getAttributes method which could return a HashMap with attribute names as keys to access the corresponding values? Circles would have "radius" keys, Rectangles would have "base" and "height" keys, and both would have "area" and "color" keys.

Upvotes: 0

Bhesh Gurung
Bhesh Gurung

Reputation: 51030

I mean how I can do to recognize that the object in the ith position is a Rectangle or a Circle ...

The easiest way to do that is by using the instanceof operator.

e.g.

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

Which is not considered a good practice (these are all your own class but still you will have to check the type of the object (at runtime) and use explicit casting).

But if all you want to do is print the attributes, then you can just let both the subclasses override the toString() properly and then you can just do -

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

Upvotes: 2

Related Questions