Guess who's inheriting the money

Guess who's inheriting the money

Biology and inheritance

Biology and inheritance

Duplicate code

public class Rectangle{
  // Center coordinate
  private double x; 
  private double y;

  public move (double dx, double dy){ 
     x += dx;
     y += dy;
  }
  private double width, height; 
  ...
}
public class Circle {
  // Center coordinate
  private double x; 
  private double y;

  public move (double dx, double dy){ 
     x += dx;
     y += dy;
  }
  private double radius; 
  ...
}

Idea: Centralize common code

  • Create a parent class Shape containing common code portions.

  • Relate both Rectangle and Circle to Shape.

Common and specific properties

Common Rectangle and Circle attributes:
double x;
double y;
Rectangle attributes Circle attributes
double width;
double height;
double radius;

Basic shape inheritance

layered SVG image

Basic shape inheritance

layered SVG image

Inheritance

  • Derived classes inherit state and behaviour.

  • Refinement, specialization.

  • is-A relationship:

    • A rectangle is a shape.

    • A circle is a shape.

Implementing Shape hierarchy

public class Shape {
  private double x, y;
}
public class Rectangle extends Shape {
  private double width;
  private double height;
}
public class Circle extends Shape {
  private double radius;
}

Creating instances

final double x = 2, y = 3;
final Shape shape = new Shape(x, y);

final double width = 2, height = 5;
final Rectangle r = new Rectangle(x, y , width, height);

final double radius = 2.5;
final Circle circle = new Circle(x, y , radius);

Shape constructor

/**
  * Creating a shape located at center coordinate.
  * @param x The center's x component.
  * @param y The center's y component.
  */
public Shape(double x,double y) {
  this.x = x;
  this.y = y;
}

Creating Rectangle instances

final Rectangle r = 
   new Rectangle(x, y ,
                 width, height );

Center coordinate components belonging to superclass Shape.

width and height belonging to class Rectangle.

Solution: Nested constructor call. Coming soon ...

Rectangle constructor

/**
  * Creating a rectangle at (x|y) of given width and height.
  * @param x Center's x component.
  * @param y Center's y component.
  * @param width Rectangle's width.
  * @param height Rectangle's height.
  */
public Rectangle(double x, double y,
             double width, double height) {
  super(x, y) ;
  this.width = width; this.height = height ;
}

Shape.equals()

public abstract class Shape {
  ...
  @Override  public boolean equals(final Object o) {
    if (o instanceof Shape ) {
      final Shape other = (Shape) o; 
      return x == other.x && y == other.y; 
    } else {
      return false; 
    } ...

Rectangle.equals()

public class Rectangle extends Shape {
  ...
  @Override public boolean equals(final Object o) {
    if (o instanceof Rectangle) {
      final Rectangle other = (Rectangle) o;
      return super.equals(o)  &&  width == other.width && height == other.height ;
    } else {
      return false;
    } ...

Shape log info

public class Run {
  static final Logger log = LogManager.getLogger(Run.class);
  public static void main(String[] args) {
    final double x = 2.0, y = 3.0;
    final Shape shape = new Shape(x, y);
    log.info(shape);  ...
2017... INFO  [main] inherit.Run ... - inherit.Shape@37d31475

Desired:

2017... INFO  [main] inherit.Run ... - (2.0|3.0)

Overwriting toString()

layered SVG image

Overwriting toString()

layered SVG image

Overwriting toString()

layered SVG image

Overwriting toString()

layered SVG image

Overwriting toString()

layered SVG image

Shape extending Object

layered SVG image

Shape extending Object

layered SVG image

Shape extending Object

layered SVG image

Logging Rectangle instances

public class Run {
  static final Logger log = LogManager.getLogger(Run.class);
  public static void main(String[] args) {
    final double x = 2, y = 3, width = 3., height = 4.;
    final Rectangle r = new Rectangle(x, y, width, height);
    log.info(r); ...
2017 ... INFO  [main] inherit.Run (Run.java:15) - (2.0|3.0)

Desired:

2017 ... INFO  Rectangle at (2.0|3.0), width= 3.0, height=4.0

Override toString() again.

public class Rectangle extends Shape {
  @Override public String toString() {
    return "Rectangle at " + super.toString()  +
      ", width= " + width + ", height=" + height; 
  } ...

The super keyword allows for calling the toString() method from superclass Shape.

Append class Rectangle's own width and height attributes.

Rectangle extending Shape

layered SVG image

Rectangle extending Shape

layered SVG image

Rectangle extending Shape

layered SVG image

Rectangle extending Shape

layered SVG image

Implementing Circle.toString()

public class Circle extends Shape {
  /**
   * Creating a circle of given center and radius
   * @param x Center's x component.
   * @param y Center's y component.
   * @param radius The circle's radius.
   */
  public Circle(double x,double y, double radius) {
    super(x, y);
    this.radius = radius;
  }
  @Override public String toString() {
    return "Circle at " + super.toString() +", radius= " + radius;
  }
  private double radius;
}

Shape and toString()

Shape and toString()

Followup exercise

No. 147: String vs. StringBuffer

Moving Shape instances

layered SVG image

Moving Shape instances

layered SVG image

Moving Shape instances

layered SVG image

Implementing Shape movements

public class Shape {
  /**
   * Move by a given translation vector
   * @param xTrans Translation's x component
   * @param yTrans Translation's y component
   */
  public void move(final int xTrans, final int yTrans) {
    x += xTrans;
    y += yTrans;
  } ...

Fools are everywhere!

public class Rectangle extends Shape {
  @Override public void move(int xTrans, int yTrans) {
    // I'm so dumb!
    ...
  }

Solution: final prevents overriding

public abstract class Shape {
... public final void move(final int xTrans, final int yTrans) {
      x += xTrans;
      y += yTrans;
    }...
public class Rectangle extends Shape {
  // Syntax error: 'move(int, int)' cannot override
  // 'move(int, int)' in 'inherit.Shape'; overridden method is final
  @Override public void move(int xTrans, int yTrans) {...

Calculating a shape's area

public class Rectangle extents Shape {
  /**
   * Calculate the area.
   * @return The rectangle's area
   */
  public double getArea() {
    return width * height;
  }
public class Circle extents Shape {
  /**
   * Calculate the area.
   * @return The circle's area
   */
  public double getArea() {
    return Math.PI * radius * radius;
  }

Desired: Polymorphic getArea() call

final Shape[] shapes  = {
    new Circle(1, 1, 2.) ,
    new Rectangle(1, -1, 2., 3.)};

for (final Shape s : shapes) {
  System.out.println(s.toString() + ": area = " + s.getArea()); 
}
Circle at (1.0|1.0), radius= 2.0: area = 12.566370614359172
Rectangle at (1.0|-1.0), width= 2.0, height=3.0: area = 6.0

Problems:

  • No meaningful getArea() method in class Shape possible.

  • Meaningful implementations exist both in subclass Rectangle and Circle.

Solution: Abstract method getArea() in superclass Shape.

abstract method getArea()

abstract public class Shape {
  /**
   * Calculate the shape's area.
   * @return The shape's area
   */
  abstract public double getArea(); ...
public class Rectangle extends Shape {
  @Override
  public double getArea() {
    return width * height;
  }...
public class Circle ... {
 @Override
  public double getArea() {
    return Math.PI *
           radius * radius;
  } ...

abstract method getArea()

layered SVG image

abstract method getArea()

layered SVG image

abstract method getArea()

layered SVG image

abstract method getArea()

layered SVG image

abstract method getArea()

layered SVG image

abstract method getArea()

layered SVG image

abstract method getArea()

layered SVG image

What's a shape anyway?

What's a “shape” anyway?

No instances of abstract classes.

final Shape s =
   new Shape(1., 2.); // 'Shape' is abstract; cannot be instantiated

Mandatory getArea() implementation.

// Error: Class 'Circle' must either be declared abstract or
//        implement abstract method 'getArea()' in 'Shape'
public class Circle extends Shape {

  public Circle(double x,double y, double radius) {
    super(x, y);
    this.radius = radius;
  }
  private double radius;
}

Facts about abstract fields, methods and classes.

  • A class containing an abstract method must itself be declared abstract.

  • abstract classes are allowed to host non-abstract methods.

  • A class may be declared abstract irrespective of purely containing non-abstract methods.

Moving shapes

Moving shapes

Followup exercises

protected access

package model;
public abstract class Shape {

  final protected long creationTime = System.nanoTime();
...
}
------------------------------------------------
package model.sub;
public class Rectangle extends Shape {
  static final Logger log = LogManager.getLogger(Rectangle.class);

  @Override public double getArea() {
    log.info("Rectangle creation time:" + creationTime );
    return width * height;
  } ...
}

Followup exercises

final classes

public final class Shape { ... }

-------------------------
public class Rectangle
  extends Shape { // Error: final class cannot be extended
...

final classes, rationale

  • Design decision.

  • Slight performance gain.

Note

Prominent Example: java.lang.String.

Defeating polymorphism

public static void main(String[] args) {
  final Shape[] shapes = {
    new Circle(1, 1, 2.),
    new Rectangle(1, -1, 2., 3.)};
  print(shapes);
}
static void print(final Shape[] shapes) {
  for (final Shape s : shapes) {
    if (s instanceof Rectangle) {
      System.out.println("Type Rectangle");
    } else if (s instanceof Circle) {
      System.out.println("Type Circle");
    }
  }
}
Type Circle
Type Rectangle

Defining equals(...): Expectations

Rectangle r1 = new Rectangle(1, 2, 5, 4),
          r2 = new Rectangle(1, 2, 1, 7),
          r3 = new Rectangle(1, 2, 5, 4);

Circle c = new Circle(-2, 3, 5);

System.out.println(r1.equals("Hello")); // false: Differing classes Rectangle and String.
System.out.println(r1.equals(r2));      // false: Differing width and height.
System.out.println(r3.equals(r1));      // true:  Two rectangles, 
                                        // identical (x|y), width and height.
System.out.println(r1.equals(c));  // false: Differing classes Rectangle and Circle.
System.out.println(c.equals(c));   // true: Object equal to itself.

Defining equals(...) of Shape instances

Two Shape instances shall be considered equal if:

  • Both instances are of common type i.e. either Rectangle or Circle.

  • Their center coordinates match within a threshold of 10 - 15 .

  • width and height or radius match within a threshold of 10 - 15 .

Comparing center coordinates

public abstract class Shape {
  private double x, y;

  protected boolean equalCenter(final Shape o) {
      return Math.abs(o.x - x) + Math.abs(o.y - y) < 1.E-15;
  }
...

Implementing Rectangle.equals()

public class Rectangle extends Shape {
    @Override public boolean equals(Object o) {
    if (o instanceof Rectangle){
      final Rectangle oRectangle = (Rectangle) o;
      return super.equalCenter(oRectangle) &&
          Math.abs(oRectangle.width- width) +
          Math.abs(oRectangle.height- height) < 1.E-15;
    }
    return false;
  }
 ...

if o == null then o instanceof Rectangle evaluates to false.

Implementing Circle.equals()

public class Circle extends Shape {
  @Override public boolean equals(final Object o) {
    if (o instanceof Circle){
      final Circle oCircle = (Circle) o;
      return super.equalCenter(o) &&
        Math.abs(oCircle.radius - radius) < 1.E-15;
    }
    return false;
  } ...

Testing equality of Shape objects

final Rectangle
    r1 = new Rectangle(2, 3, 1,4),
    r2 = new Rectangle(2, 3, 2,8),
    r3 = new Rectangle(2, 3, 1,4);

final Circle c = new Circle(2,3, 7);

System.out.println("r1.equals(r2): " + r1.equals(r2));
System.out.println("r1.equals(r3): " + r1.equals(r3));
System.out.println("c.equals(r1): " + c.equals(r1));
r1.equals(r2): false
r1.equals(r3): true
c.equals(r1): false

Overriding Object.toString()

public class Shape {

  double x, y;
 ...
  @Override  // Promise: Subsequent method overrides Object.toString();
  public String toString() {
    return "(" + x + "|" + y + ")";
  }
}

Failure: Not overriding Object.toString()

public class Shape {

  double x, y;
 ...
  @Override  // Error: method does not override a method from a supertype
  public String toString(int value) {
    return "(" + x + "|" + y + ")";
  }
}