4.1.2.3 - Inheritance, polymorphism and object relationships (Java)

4.1.2.3 - Inheritance, polymorphism and object relationships (Java)

Learn how Java subclasses inherit and override behaviour, how a common reference type supports polymorphism, and when a “has a” relationship is a better design. Interpret UML relationship symbols and apply the three specified object-oriented design principles. This Java route is incomplete: runnable support pending; source tasks are written practice with no Run.

Inheritance and overriding

Inheritance defines a subclass as a specialised form of a superclass: a Rectangle is a Shape. Java uses extends for single class inheritance. The subclass can reuse accessible behaviour and provide specialised behaviour, while a superclass constructor initialises the superclass part of the object.

Java
abstract class Shape {
    protected String name;

    public Shape(String name) {
        this.name = name;
    }

    public String description() {
        return name;
    }

    public abstract int area();
}

class Rectangle extends Shape {
    private int width;
    private int height;

    public Rectangle(int width, int height) {
        super("Rectangle");
        this.width = width;
        this.height = height;
    }

    @Override
    public int area() {
        return width * height;
    }

    @Override
    public String description() {
        return name + " " + width + " by " + height;
    }
}

super("Rectangle") calls the superclass constructor; constructors are not inherited as ordinary methods. Each Rectangle stores its own dimensions and superclass name. The protected name can be used by this subclass, while private width and height remain controlled by Rectangle. A superclass's private state can still exist in the object even though subclass code cannot access it directly by name.

Overriding supplies a subclass implementation of an inherited instance method with the matching signature and compatible return type. @Override lets the compiler check this intention. Here Rectangle implements the abstract area operation and overrides the concrete description. Changing the parameter list would instead create an overload, a different method, rather than override the same contract. An overriding method cannot reduce the inherited access level. Static methods are hidden rather than dynamically overridden.

For new Rectangle(3, 4), area returns 12 and description returns Rectangle 3 by 4. Inheritance is justified by a genuine subtype relationship, not merely a wish to reuse a few lines.

Polymorphism

Polymorphism lets code use a common type while different concrete objects supply their appropriate behaviour. The reference's declared type determines which operations the source may request; the receiving object's runtime class selects an overridden instance implementation.

Java
class Square extends Shape {
    private int side;

    public Square(int side) {
        super("Square");
        this.side = side;
    }

    @Override
    public int area() {
        return side * side;
    }
}

Using the preceding Shape and Rectangle definitions:

Java
Shape[] shapes = {new Rectangle(3, 4), new Square(5)};
int total = 0;
for (Shape shape : shapes) {
    total += shape.area();
}

The first call invokes Rectangle.area and contributes 12; the second invokes Square.area and contributes 25, leaving total 37. The loop does not need a branch for every kind of shape. Adding a new concrete Shape with a valid area implementation can leave this loop unchanged. The Shape reference does not turn a Rectangle object into an abstract Shape object; it refers to that existing Rectangle through a common type.

The abstract contract ensures each concrete shape supplies area, but Java cannot prove the formula is mathematically correct. Tests still check the implementation. A subclass is a usable substitute only if it honours the superclass's promised meaning and preconditions; sharing a method name alone is insufficient.

Aggregation and composition

A “has a” relationship means one object uses or contains another, rather than being a subtype of it. Aggregation models parts that can exist independently of the whole. Composition models stronger ownership: the part belongs to the whole's lifecycle in the design. UML places a white diamond at the whole for aggregation and a black diamond at the whole for composition.

For aggregation, a Team can refer to Player objects created independently and also known elsewhere:

Java
class Player {
    private String name;

    public Player(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class Team {
    private Player captain;

    public Team(Player captain) {
        this.captain = captain;
    }

    public String captainName() {
        return captain.getName();
    }
}

Continue this lesson.

You’re previewing NovaLearn without an account. Create a free account to read the rest.

Create free account