Cross Training Home Development Chat Log Java Upgrade 1 Showcase Java Upgrade 1 Analysis Java Upgrade 2 Showcase Java Upgrade 2 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 twice. Upgrade 1 stores roots as OrderedPair objects, adds a shared DecimalFormat, and fixes double comparisons with EPSILON. Upgrade 2 adds final constants, an explicit extends Object, axis of symmetry, a memoryAddress ivar via super.toString(), and a live aliasing trap demonstration. See: Upgrade 1 Showcase, Upgrade 1 Analysis, Upgrade 2 Showcase, Upgrade 2 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.