Runtime polymorphism

Figure 515. Polymorphism: Iteration over Object instances Slide presentation
Code
final Object[] instances = {
  new String("Test"),
  LocalTime.of(16, 30),
  new Circle(2.0, 3.0, 7)
};
print(instances);
static void print(Object[] objects) {
  for (final Object o: objects) {
    IO.println(o); // calling o.toString()
  }
}
Result
Test
16:30
(2.0|3.0), radius=7.0

Figure 516. Meta info of an instance Slide presentation

How does calling toString() work?


Figure 517. Instance meta info Slide presentation
Code
final Object[] instances = {
  new String("Test"),
  LocalTime.of(16, 30),
  new Circle(2.0, 3.0, 7)
};
print(instances);
static void printMetaInfo(Object[] objects) {
  for (final Object o: objects) {
    IO.println(o.getClass().getName());
  }
}
Result
java.lang.String
java.time.LocalTime
de.hdm_stuttgart.mi.sd1.Circle

Figure 518. Polymorphic dispatch / late binding Slide presentation

Steps during run time:

  1. Determine object's type.

  2. Select corresponding method in inheritance hierarchy.

  3. Execute method.


Figure 519. Example: Executing move() Slide presentation
final Shape circle = new Circle(2.0, 3.0, 7.0);

circle.move(5.0, 2.0);

Which method is being executed?


Figure 520. Implementing getArea() Slide presentation
public class Rectangle extends Shape {
    ...
  private int width, height;
  @Override public double getArea() {
        return width * height;
  }
}
public class Circle extends Shape {
    ...
    private int radius;

    @Override public double getArea() {
        return Math.PI * radius * radius;
    }
}

Figure 521. Implementing getArea() in class Shape ? Slide presentation
public abstract class Shape {
...
  public double getArea() {
     return ??? ;
  }
}

Figure 522. Solution: Abstract method getArea() in abstract class Shape Slide presentation
public abstract class Shape {
...
    // No implementation body {...}
    abstract public double getArea();
}
public class Rectangle extends Shape {
    ...
  private int width, height;
  @Override public double getArea() {
        return width * height; 
  }
}
public class Circle extends Shape {
    ...
    private int radius;

    @Override public double getArea() {
        return Math.PI * radius * radius;
    }
}

Figure 523. Polymorphic getArea() call Slide presentation
Code
final Shape[] shapes = {
  new Rectangle(0, 0, 2, 3),
  new Circle(0, 0, 1)};
}
printAreas(shapes);
static void printAreas(Shape[] shapes) {
  for (Shape shape : shapes) {
    IO.println(shape.getArea());
  }
}
Result
Area:6.0
Area:3.141592653589793

Figure 524. Runtime polymorphic dispatch Slide presentation

Figure 525. Polymorphic dispatch pseudo implementation Slide presentation
public abstract class Shape {
...
public final double getArea(){
  if (this instanceof Rectangle rectangle) {
    return rectangle.width * rectangle.height;
  } else if (this instanceof Circle circle) {
    return circle.radius * circle.radius * Math.PI;
  } else {
    return 0; // Unreachable
  }
}
public class Rectangle extends Shape {
  protected int width, height;  ...
}
public class Circle extends Shape {
    protected int radius; ...
}