Cross Training Home Development Chat Log Java Upgrade Showcase Java Upgrade Analysis Concept #8 — Multiple Classes, Inheritance & Class Anatomy
TNT  ›  Cross Training  ›  Multiple Classes, Inheritance & Class Anatomy
Concept #8

Multiple Classes, Inheritance & Class Anatomy

A class can use another class (composition) or extend it (inheritance).
Three classes — OrderedPair, ComplexOrderedPair, Quadratic — show both patterns,
anchored to the PCNICOTGSU rule for well-formed classes.

Classes Working Together

A single class packages one concept. Real programs package many. This page builds three classes that work together: one from Concept #6 (slightly extended), one that inherits from it, and one that uses both — each exposing a different OOP relationship.

Composition is when a class holds an instance of another class as an instance variable. Quadratic has a vertex field of type OrderedPair. The quadratic does not become an ordered pair; it contains one. Composition models “has-a” relationships.

Inheritance is when a class extends another class, acquiring all of its fields and methods automatically. ComplexOrderedPair extends OrderedPair: every getter, every constructor path, every utility method of OrderedPair is available in ComplexOrderedPair for free. The child then overrides toString() to display a + bi instead of (a, b), and adds two new methods — conjugate() and modulus() — that have no meaning in the parent but are essential for complex numbers. Inheritance models “is-a” relationships.

When Quadratic calculates roots and the discriminant is negative, it creates ComplexOrderedPair objects to represent the complex roots. That single line connects all three classes: the inheritance hierarchy provides the complex number type; the composition provides the host object that requests it.

The PCNICOTGSU mnemonic is the teacher’s shorthand for the ten anatomy elements every well-formed class should have. It is embedded as a comment in the Java and JavaScript versions so you can see each element in place. The Quadratic class is designed to be a reference example: every letter of the mnemonic has at least one corresponding line of code.

There’s more to this story. The Java version of Quadratic was later upgraded to store roots as actual OrderedPair and ComplexOrderedPair objects, add a shared DecimalFormat, and fix double comparisons with EPSILON — a look at how a teaching example grows into production-quality code. See the Java Upgrade Showcase and the Teacher’s Analysis.

Inheritance chain & composition map

// INHERITANCE: ComplexOrderedPair IS-A OrderedPair ComplexOrderedPair extends OrderedPair   inherits ── x, y, absVal, label   inherits ── getX(), getY(), getAbsVal(), getLabel()   inherits ── setLabel(), computeAbsVal(), transpose()   overrides — toString() → shows “a + bi”   adds ─────── conjugate() → ComplexOrderedPair   adds ─────── modulus() → double // COMPOSITION: Quadratic HAS-A OrderedPair (vertex) Quadratic uses OrderedPair   _vertex: OrderedPair — set by computeVertex() // COLLABORATION: Quadratic CREATES ComplexOrderedPair Quadratic creates ComplexOrderedPair   when disc < 0, getRootsDescription() instantiates   two ComplexOrderedPair objects for the root pair

PCNICOTGSU — 10 anatomy elements

Perhaps public (access modifier) Clown class (keyword) Nonsense → class Name (Quadratic, OrderedPair…) Is → private Instance variables (ivars) Constructive Constructors (default, full, copy) Only Overridden toString() Toward → (ToString — pair with O) Getting Getters (read private ivars) Settlers Setters (validate + side effects) Underwear Utility methods (f(), computeVertex()…)

Every code panel below is annotated with these labels. Find each letter in the Quadratic class source.

Calling the Classes — Driver Programs

These files exercise all three constructors, getRootsDescription() across all three discriminant cases, and setter side effects. Run them after loading the four class files.

JavaScript quadraticRunner.js
// Paste OrderedPair.js, ComplexOrderedPair.js,
// Quadratic.js, then this file — DevTools Console.

console.log("=== Quadratic Runner ===\n");

console.log("--- Default: f(x) = x\u00B2 ---");
var q1 = new Quadratic();
console.log(q1.toString());
console.log("f(3) = " + q1.f(3));
console.log("Roots: " + q1.getRootsDescription());

console.log("\n--- Two real roots: x\u00B2 - 5x + 6 ---");
var q2 = new Quadratic(1, -5, 6);
console.log(q2.toString());
console.log("Roots: " + q2.getRootsDescription());

console.log("\n--- One repeated root: x\u00B2 - 6x + 9 ---");
var q3 = new Quadratic(1, -6, 9);
console.log(q3.toString());
console.log("Roots: " + q3.getRootsDescription());

console.log("\n--- Complex roots: x\u00B2 + 2x + 5 ---");
var q4 = new Quadratic(1, 2, 5);
console.log(q4.toString());
console.log("Roots: " + q4.getRootsDescription());

console.log("\n--- Setter side effects ---");
var q5 = new Quadratic(1, 0, -4);
console.log("Before: " + q5.toString());
q5.setB(-4);
console.log("After setB(-4): " + q5.toString());

console.log("\n--- Copy constructor ---");
var q6 = new Quadratic(q2);
console.log("Copy of q2: " + q6.toString());

console.log("\n--- Thanks for using our program! ---");
Java QuadraticDriver.java
// JDoodle multi-file (Java): 4 files required —
//   File 1: OrderedPair.java
//   File 2: ComplexOrderedPair.java
//   File 3: Quadratic.java
//   File 4: QuadraticDriver.java ← set as main file
public class QuadraticDriver {
    public static void main(String[] args){
        System.out.println("=== Quadratic Driver ===\n");

        System.out.println("--- Default: f(x) = x\u00B2 ---");
        Quadratic q1 = new Quadratic();
        System.out.println(q1);
        System.out.println("f(3) = " + q1.f(3));
        System.out.println("Roots: " + q1.getRootsDescription());

        System.out.println(
            "\n--- Two real roots: x\u00B2 - 5x + 6 ---");
        Quadratic q2 = new Quadratic(1, -5, 6);
        System.out.println(q2);
        System.out.println("Roots: " + q2.getRootsDescription());

        System.out.println(
            "\n--- One repeated root: x\u00B2 - 6x + 9 ---");
        Quadratic q3 = new Quadratic(1, -6, 9);
        System.out.println(q3);
        System.out.println("Roots: " + q3.getRootsDescription());

        System.out.println(
            "\n--- Complex roots: x\u00B2 + 2x + 5 ---");
        Quadratic q4 = new Quadratic(1, 2, 5);
        System.out.println(q4);
        System.out.println("Roots: " + q4.getRootsDescription());

        System.out.println("\n--- Setter side effects ---");
        Quadratic q5 = new Quadratic(1, 0, -4);
        System.out.println("Before: " + q5);
        q5.setB(-4);
        System.out.println("After setB(-4): " + q5);

        System.out.println("\n--- Copy constructor ---");
        Quadratic q6 = new Quadratic(q2);
        System.out.println("Copy of q2: " + q6);

        System.out.println(
            "\n--- Thanks for using our program! ---");
    }//end main
}//end class QuadraticDriver
Python 3 quadratic_main.py
# Add OrderedPair.py, ComplexOrderedPair.py,
# and Quadratic.py as extra files in OnlineGDB.
from Quadratic import Quadratic

def main():
    print("=== Quadratic Main ===\n")

    print("--- Default: f(x) = x\u00B2 ---")
    q1 = Quadratic()
    print(q1)
    print(f"f(3) = {q1.f(3)}")
    print("Roots:", q1.get_roots_description())

    print("\n--- Two real roots: x\u00B2 - 5x + 6 ---")
    q2 = Quadratic(1, -5, 6)
    print(q2)
    print("Roots:", q2.get_roots_description())

    print("\n--- One repeated root: x\u00B2 - 6x + 9 ---")
    q3 = Quadratic(1, -6, 9)
    print(q3)
    print("Roots:", q3.get_roots_description())

    print("\n--- Complex roots: x\u00B2 + 2x + 5 ---")
    q4 = Quadratic(1, 2, 5)
    print(q4)
    print("Roots:", q4.get_roots_description())

    print("\n--- Setter side effects ---")
    q5 = Quadratic(1, 0, -4)
    print("Before:", q5)
    q5.set_b(-4)
    print("After set_b(-4):", q5)

    print("\n--- Copy constructor ---")
    q6 = Quadratic.from_quadratic(q2)
    print("Copy of q2:", q6)

    print("\n--- Thanks for using our program! ---")

if __name__ == "__main__":
    main()

OrderedPair — the Foundation

This is Concept #6’s OrderedPair with two additions: getX() and getY(). These getters are required so ComplexOrderedPair’s copy constructor can read the parent’s private coordinates. For JDoodle, this is File 1 of 4 — paste it first before the other three files.

JavaScript OrderedPair.js
// getX() and getY() added for subclass access
class OrderedPair {

    constructor(xOrOrig, y) {
        if (xOrOrig instanceof OrderedPair) {
            console.log("...OrderedPair copy constructor...");
            this._x      = xOrOrig._x;
            this._y      = xOrOrig._y;
            this._absVal = this._computeAbsVal();
            this._label  = xOrOrig._label + "_copy";
        } else if (xOrOrig !== undefined
                   && y !== undefined) {
            console.log("...OrderedPair two-parameter constructor...");
            this._x      = xOrOrig;
            this._y      = y;
            this._absVal = this._computeAbsVal();
            this._label  = "P";
        } else {
            console.log("...OrderedPair default constructor...");
            this._x      = 0;
            this._y      = 0;
            this._absVal = this._computeAbsVal();
            this._label  = "O";
        }
    }

    toString() {
        return this._label
            + "(" + this._x + ", " + this._y + ")";
    }
    getX()      { return this._x;      }
    getY()      { return this._y;      }
    getAbsVal() { return this._absVal; }
    getLabel()  { return this._label;  }

    setLabel(lbl) {
        console.log("...setLabel...");
        this._label = lbl;
    }
    _computeAbsVal() {
        console.log("...computeAbsVal...");
        return Math.sqrt(
            Math.pow(this._x, 2) + Math.pow(this._y, 2));
    }
    transpose() {
        console.log("...transpose...");
        var temp = this._y;
        this._y  = this._x;
        this._x  = temp;
        this._absVal = this._computeAbsVal();
    }

}//end class OrderedPair
Java OrderedPair.java
// Concept #6 + getX() / getY() for subclass access
public class OrderedPair {
    private double x;
    private double y;
    private double absVal;
    private String label;

    public OrderedPair(){
        System.out.println("...OrderedPair default constructor...");
        x = 0; y = 0;
        absVal = computeAbsVal();
        label = "O";
    }
    public OrderedPair(double x, double y){
        System.out.println(
            "...OrderedPair two-parameter constructor...");
        this.x = x; this.y = y;
        absVal = computeAbsVal();
        label = "P";
    }
    public OrderedPair(OrderedPair orig){
        System.out.println("...OrderedPair copy constructor...");
        x = orig.x; y = orig.y;
        absVal = computeAbsVal();
        label = orig.label + "_copy";
    }
    public String toString(){
        return label + "(" + x + ", " + y + ")";
    }
    public double getX()      { return x;      }
    public double getY()      { return y;      }
    public double getAbsVal() { return absVal; }
    public String getLabel()  { return label;  }
    public void setLabel(String lbl){
        System.out.println("...setLabel...");
        label = lbl;
    }
    public double computeAbsVal(){
        System.out.println("...computeAbsVal...");
        return Math.sqrt(
            Math.pow(x, 2) + Math.pow(y, 2));
    }
    public void transpose(){
        System.out.println("...transpose...");
        double temp = y; y = x; x = temp;
        absVal = computeAbsVal();
    }
}//end class OrderedPair
Python 3 OrderedPair.py
import math

class OrderedPair:
    """Concept #6 + get_x() / get_y() for subclass access."""

    def __init__(self, x=0.0, y=0.0):
        print("...OrderedPair constructor...")
        self._x       = x
        self._y       = y
        self._abs_val = self._compute_abs_val()
        self._label   = "O" \
            if (x == 0.0 and y == 0.0) else "P"

    @classmethod
    def from_ordered_pair(cls, orig):
        """Copy constructor."""
        print("...OrderedPair copy constructor (classmethod)...")
        instance        = cls(orig._x, orig._y)
        instance._label = orig._label + "_copy"
        return instance

    def __str__(self):
        return (self._label
            + "(" + str(self._x)
            + ", " + str(self._y) + ")")

    def get_x(self):       return self._x
    def get_y(self):       return self._y
    def get_abs_val(self): return self._abs_val
    def get_label(self):   return self._label

    def set_label(self, lbl):
        print("...set_label...")
        self._label = lbl

    def _compute_abs_val(self):
        print("...compute_abs_val...")
        return math.sqrt(self._x ** 2 + self._y ** 2)

    def transpose(self):
        print("...transpose...")
        self._x, self._y = self._y, self._x
        self._abs_val = self._compute_abs_val()

#end class OrderedPair

ComplexOrderedPair extends OrderedPair

Inherits everything from OrderedPair. Overrides toString() to show a + bi. Adds conjugate() and modulus(). No new fields needed — x is the real part, y is the imaginary part.

JavaScript ComplexOrderedPair.js
// x = real part, y = imaginary part of a + bi
class ComplexOrderedPair extends OrderedPair {

    constructor(xOrOrig, y) {
        if (xOrOrig instanceof ComplexOrderedPair) {
            // copy path
            console.log("...ComplexOrderedPair copy constructor...");
            super(xOrOrig.getX(), xOrOrig.getY());
            this.setLabel(xOrOrig.getLabel());

        } else if (xOrOrig !== undefined && y !== undefined) {
            // real + imaginary
            console.log("...ComplexOrderedPair two-parameter constructor...");
            super(xOrOrig, y);
            this.setLabel("z");

        } else {
            // default: 0 + 0i
            console.log("...ComplexOrderedPair default constructor...");
            super();
            this.setLabel("z");
        }
    }

    // Override toString — a+bi notation
    toString() {
        var r = this.getX();
        var i = this.getY();
        if (i === 0)  return "" + r;
        if (r === 0)  return i + "i";
        if (i > 0)    return r + " + " + i + "i";
        return r + " - " + Math.abs(i) + "i";
    }

    // New methods added by the subclass
    conjugate() {
        return new ComplexOrderedPair(this.getX(), -this.getY());
    }
    modulus() { return this.getAbsVal(); }

}//end class ComplexOrderedPair
Java ComplexOrderedPair.java
// extends = inherits all OrderedPair fields and methods
public class ComplexOrderedPair extends OrderedPair {

    // default constructor: 0 + 0i
    public ComplexOrderedPair(){
        super();          // calls OrderedPair()
        setLabel("z");
    }

    // two-parameter: real + imaginary
    public ComplexOrderedPair(double real, double imaginary){
        super(real, imaginary);  // calls OrderedPair(x,y)
        setLabel("z");
    }

    // copy constructor — must use getters (orig.x is private!)
    public ComplexOrderedPair(ComplexOrderedPair orig){
        super(orig.getX(), orig.getY());
        setLabel(orig.getLabel());
    }

    // @Override toString — show a+bi instead of (real, imag)
    @Override
    public String toString(){
        double r = getX();   // inherited getter
        double i = getY();   // inherited getter
        if (i == 0)  return "" + r;
        if (r == 0)  return i + "i";
        if (i > 0)   return r + " + " + i + "i";
        return r + " - " + Math.abs(i) + "i";
    }

    // New behavior added by the subclass
    public ComplexOrderedPair conjugate(){
        return new ComplexOrderedPair(getX(), -getY());
    }
    public double modulus(){ return getAbsVal(); }

}//end class ComplexOrderedPair
Python 3 ComplexOrderedPair.py
from OrderedPair import OrderedPair
import math

# (ClassName) = inherits from OrderedPair
class ComplexOrderedPair(OrderedPair):
    """x = real part, y = imaginary part."""

    def __init__(self, real=0.0, imaginary=0.0):
        super().__init__(real, imaginary)  # calls OrderedPair.__init__
        self._label = "z"
        print("...ComplexOrderedPair constructor...")

    @classmethod
    def from_complex(cls, orig):
        """Copy constructor."""
        instance        = cls(orig.get_x(), orig.get_y())
        instance._label = orig.get_label()
        return instance

    # Override __str__ — a+bi notation
    def __str__(self):
        r = self.get_x()
        i = self.get_y()
        if i == 0:   return str(r)
        if r == 0:   return str(i) + "i"
        if i > 0:    return str(r) + " + " + str(i) + "i"
        return str(r) + " - " + str(abs(i)) + "i"

    # New methods added by the subclass
    def conjugate(self):
        return ComplexOrderedPair(self.get_x(), -self.get_y())

    def modulus(self):
        return self.get_abs_val()  # inherited

#end class ComplexOrderedPair

Quadratic — the Full PCNICOTGSU Class

Every PCNICOTGSU element is present and annotated. Note how the setters call computeDiscriminant() and computeVertex() as side effects — a direct application of the DWR lesson from Concept #7.

JavaScript Quadratic.js
// Quadratic: f(x) = ax\u00B2 + bx + c
// PCNICOTGSU anatomy annotated throughout

class Quadratic {  // P C N

    constructor(aOrOrig, b, c) {  // C
        if (aOrOrig instanceof Quadratic) {
            this._a = aOrOrig._a; this._b = aOrOrig._b;
            this._c = aOrOrig._c;
        } else if (aOrOrig !== undefined) {
            if (aOrOrig === 0) throw new Error("a\u22600");
            this._a = aOrOrig; this._b = b; this._c = c;
        } else {
            this._a = 1; this._b = 0; this._c = 0;
        }
        this._computeDiscriminant();
        this._computeVertex();
    }

    toString() {  // O/T
        return "f(x) = " + this._a + "x\u00B2 + "
             + this._b + "x + " + this._c
             + " | vertex: " + this._vertex.toString()
             + " | disc: " + this._discriminant;
    }

    // Getters  G
    getA()            { return this._a;            }
    getB()            { return this._b;            }
    getC()            { return this._c;            }
    getDiscriminant() { return this._discriminant; }
    getVertex()       { return this._vertex;       }

    // Setters — validate + side effects  S
    setA(a) {
        if (a === 0) throw new Error("a\u22600");
        this._a = a;
        this._computeDiscriminant(); this._computeVertex();
    }
    setB(b) { this._b = b; this._computeDiscriminant(); this._computeVertex(); }
    setC(c) { this._c = c; this._computeDiscriminant(); this._computeVertex(); }

    // Utility methods  U
    f(x) { return this._a*x*x + this._b*x + this._c; }

    _computeDiscriminant() {
        this._discriminant = this._b*this._b - 4*this._a*this._c;
    }
    _computeVertex() {
        var h = -this._b / (2 * this._a);
        this._vertex = new OrderedPair(h, this.f(h));
        this._vertex.setLabel("V");
    }

    getRootsDescription() {
        var d = this._discriminant;
        if (d > 0) {
            var r1 = (-this._b + Math.sqrt(d)) / (2*this._a);
            var r2 = (-this._b - Math.sqrt(d)) / (2*this._a);
            return "Two real: x=" + r1 + " and x=" + r2;
        } else if (d === 0) {
            return "One root: x=" + (-this._b/(2*this._a));
        } else {
            var rp = -this._b/(2*this._a);
            var ip = Math.sqrt(-d)/(2*this._a);
            return "Complex: " +
                new ComplexOrderedPair(rp, ip).toString() +
                " and " +
                new ComplexOrderedPair(rp, -ip).toString();
        }
    }
}//end class Quadratic
Java Quadratic.java
//(P)erhaps (C)lown (N)onsense (I)s (C)onstructive
//(O)nly (T)oward (G)etting (S)ettlers (U)nderwear
public class Quadratic {        // P  C  N

    // Ivars ─────────────────────────────────── I
    private double a, b, c;
    private double discriminant;
    private OrderedPair vertex;  // another class as ivar!

    // Constructors ───────────────────────────── C
    public Quadratic(){
        a=1; b=0; c=0;
        computeDiscriminant(); computeVertex();
    }
    public Quadratic(double a, double b, double c){
        if (a==0) throw new
            IllegalArgumentException("a cannot be zero");
        this.a=a; this.b=b; this.c=c;
        computeDiscriminant(); computeVertex();
    }
    public Quadratic(Quadratic orig){
        a=orig.a; b=orig.b; c=orig.c;
        computeDiscriminant(); computeVertex();
    }

    // toString ───────────────────────────────── O/T
    @Override public String toString(){
        return "f(x)="+a+"x\u00B2+"+b+"x+"+c
             +" vertex:"+vertex+" disc:"+discriminant;
    }

    // Getters ────────────────────────────────── G
    public double getA()           { return a;            }
    public double getDiscriminant(){ return discriminant; }
    public OrderedPair getVertex() { return vertex;       }

    // Setters — validate + side effects ──────── S
    public void setA(double a){
        if (a==0) throw new
            IllegalArgumentException("a cannot be zero");
        this.a=a;
        computeDiscriminant(); // side effect
        computeVertex();       // side effect
    }
    public void setB(double b){
        this.b=b; computeDiscriminant(); computeVertex();
    }
    public void setC(double c){
        this.c=c; computeDiscriminant(); computeVertex();
    }

    // Utility methods ────────────────────────── U
    public double f(double x){ return a*x*x + b*x + c; }

    private void computeDiscriminant(){
        discriminant = b*b - 4*a*c;
    }
    private void computeVertex(){
        double h = -b/(2*a);
        vertex = new OrderedPair(h, f(h));
        vertex.setLabel("V");
    }
    public String getRootsDescription(){
        if (discriminant > 0){
            double r1=(-b+Math.sqrt(discriminant))/(2*a);
            double r2=(-b-Math.sqrt(discriminant))/(2*a);
            return "Two real: x="+r1+" and x="+r2;
        } else if (discriminant == 0){
            return "One root: x="+(-b/(2*a));
        } else {
            // disc < 0: ComplexOrderedPair enters the chat!
            double rp=-b/(2*a);
            double ip=Math.sqrt(-discriminant)/(2*a);
            return "Complex: "
                +new ComplexOrderedPair(rp,ip)
                +" and "+new ComplexOrderedPair(rp,-ip);
        }
    }
}//end class Quadratic
Python 3 Quadratic.py
from OrderedPair import OrderedPair
from ComplexOrderedPair import ComplexOrderedPair
import math

class Quadratic:
    """f(x) = ax\u00B2 + bx + c  (a \u2260 0)."""

    def __init__(self, a=1.0, b=0.0, c=0.0):  # C
        if a == 0: raise ValueError("a \u22600")
        self._a = a; self._b = b; self._c = c
        self._discriminant = 0.0
        self._vertex = None
        self._compute_discriminant()
        self._compute_vertex()

    @classmethod
    def from_quadratic(cls, orig):  # C copy
        return cls(orig._a, orig._b, orig._c)

    def __str__(self):  # O/T
        return (f"f(x)={self._a}x\u00B2+{self._b}x+{self._c}"
                f" vertex:{self._vertex}"
                f" disc:{self._discriminant}")

    # Getters  G
    def get_a(self):            return self._a
    def get_discriminant(self): return self._discriminant
    def get_vertex(self):       return self._vertex

    # Setters — validate + side effects  S
    def set_a(self, a):
        if a == 0: raise ValueError("a \u22600")
        self._a = a
        self._compute_discriminant()  # side effect
        self._compute_vertex()        # side effect

    def set_b(self, b):
        self._b = b
        self._compute_discriminant()
        self._compute_vertex()

    def set_c(self, c):
        self._c = c
        self._compute_discriminant()
        self._compute_vertex()

    # Utility methods  U
    def f(self, x):
        return self._a*x**2 + self._b*x + self._c

    def _compute_discriminant(self):
        self._discriminant = self._b**2 - 4*self._a*self._c

    def _compute_vertex(self):
        h = -self._b / (2 * self._a)
        self._vertex = OrderedPair(h, self.f(h))
        self._vertex.set_label("V")

    def get_roots_description(self):
        d = self._discriminant
        if d > 0:
            r1 = (-self._b + math.sqrt(d)) / (2*self._a)
            r2 = (-self._b - math.sqrt(d)) / (2*self._a)
            return f"Two real: x={r1} and x={r2}"
        elif d == 0:
            return f"One root: x={-self._b/(2*self._a)}"
        else:
            rp = -self._b / (2*self._a)
            ip = math.sqrt(-d) / (2*self._a)
            z1 = ComplexOrderedPair(rp,  ip)
            z2 = ComplexOrderedPair(rp, -ip)
            return f"Complex: {z1} and {z2}"

#end class Quadratic

Why Does Each Version Look Different?

“What does each version reveal about the language’s personality?”

JavaScript

One constructor, all three paths — same as Concept #6. JavaScript allows exactly one constructor per class. The inheritance pattern via extends and super() is identical in syntax to Java, but the enforcement is not: nothing in JavaScript prevents you from bypassing the gate. _computeVertex is private by convention, not enforcement.

extends in ES6 is real prototype-based inheritance. When ComplexOrderedPair extends OrderedPair, JavaScript builds a prototype chain. this.getX() in ComplexOrderedPair walks up that chain to OrderedPair.prototype.getX. The method lookup is dynamic: a subclass method shadows a parent method with the same name, which is exactly how toString() override works.

Java

Constructor overloading + @Override guarantee. Java’s three separate constructors are distinct, compiler-resolved signatures. The @Override annotation on toString() is not decoration — the compiler verifies that a method with this exact signature actually exists in the parent. If you misspell it or change the return type, the annotation causes a compile error rather than a silent shadow. That is a safety net JavaScript cannot offer.

Copy constructor accesses the parent’s private fields directly. In Quadratic’s copy constructor, this.a = orig.a compiles because both objects are the same class — Java allows private access within the same class regardless of which instance. In ComplexOrderedPair’s copy constructor, orig.x would fail because x is private in OrderedPair, a different class. Getters are required. This is the distinction that forces the getX() and getY() additions.

Python 3

Single-argument super().__init__() — no class name, no self, no repeated ceremony. Python 3 resolves the Method Resolution Order (MRO) automatically. In contrast to Java’s explicit super(real, imaginary), Python’s cooperative super().__init__(real, imaginary) participates in the full inheritance chain, which matters in multiple-inheritance scenarios Python supports and Java does not.

from_quadratic is a @classmethod, not a constructor overload. Python has one __init__. The copy-constructor pattern requires a factory classmethod. This is the same pattern seen in Concept #6. It is more explicit than Java (you type Quadratic.from_quadratic(q2), not just new Quadratic(q2)) but reveals Python’s philosophy: named factory methods communicate intent clearly; overloaded constructors rely on type matching.