Cross Training Home Development Chat Log Concept #6 — Classes & OOP
TNT  ›  Cross Training  ›  Classes & OOP
Concept #6

easter egg icon Classes & OOP

Package state (fields) and behavior (methods) into a single blueprint.
Every object the driver creates is an instance of the same class —
seen here in drivers and class definitions, side by side, in three languages.

What a Class Actually Is

A class is a blueprint. A class definition is like a recipe for a cookie-cutter machine: it describes exactly what properties every object will have and what actions every object can perform. When you call new OrderedPair(x, y), you activate the blueprint and produce one specific object — one cookie — with its own x, y, label, and distance from the origin.

The OrderedPair class packages four properties and six behaviors into a single, self-maintaining unit. The properties (fields) are private: nothing outside the class can reach ptA.absVal directly. The class controls its own state, exposing only what it chooses through getters and a setter. That control is encapsulation — the first principle OOP was designed to enforce.

The three constructors reveal another OOP essential: multiple construction paths. The same class can produce an origin, a specific point, or a transposed copy — depending on what you pass. Every constructor calls computeAbsVal(), which means the object always maintains its own integrity from the moment it is built.

This concept page shows the driver first — the code that creates objects and calls methods — then the class definition. Read them together: the driver shows what the class does from the outside; the blueprint shows how it does it on the inside.

The class anatomy, before any language

CLASS OrderedPair

── FIELDS (private) ─────────────────────────────────
x, y ← double (the coordinates)
absVal ← double (√x²+y² — distance from origin)
label ← String (name of this point)

── CONSTRUCTORS ─────────────────────────────────────
OrderedPair() ← default: origin (0,0), label = "O"
OrderedPair(x, y) ← place at coords, label = "P"
OrderedPair(orig) ← copy: label = orig.label + "_transpose"

── GETTERS (read-only access) ────────────────────────
getAbsVal() → number
getLabel() → string

── SETTER (one controlled mutation from outside) ─────
setLabel(lbl)

── UTILITY METHODS ──────────────────────────────────
computeAbsVal() ← √(x²+y²) — called by each constructor
transpose() ← swap x ↔ y in place

Every code panel below is a direct translation of this anatomy. Fields become instance variables; constructors become constructor methods; the class controls what gets in and what gets out.

Calling the Class — Driver Programs

These files create objects and call methods. They never touch a field directly — every read goes through a getter. Compare how each language calls the same three constructor paths.

JavaScript orderedPairRunner.js
// Paste OrderedPair.js into DevTools Console
// first, then paste this file.

// Return function — mirrors Java's static double f(x)
function f(x) {
    console.log("...f...");
    var m = 3.0;
    var b = 2.0;
    return m * x + b;
}//end f

console.log("=== Ordered Pair Runner ===\n");

var x = 2.0;
var y = f(x);   // y = 8.0 — captured
console.log("f(" + x + ") = " + y);

console.log("\n--- Creating the Origin ---");
var origin = new OrderedPair();
console.log("origin = " + origin.toString());

console.log("\n--- Creating a Point ---");
var ptA = new OrderedPair(x, y);
console.log("ptA = " + ptA.toString());
ptA.setLabel("A");
console.log("ptA = " + ptA.toString());
//console.log(ptA._absVal); // works but breaks encapsulation
console.log("AbsVal ptA = " + ptA.getAbsVal());

console.log("\n--- Creating a Transposed Point ---");
var ptA_t = new OrderedPair(ptA); // instanceof → copy path
ptA_t.transpose();
console.log("ptA_transpose = " + ptA_t.toString());
console.log("AbsVal " + ptA_t.getLabel()
    + " = " + ptA_t.getAbsVal());

console.log("\n--- Thanks for using our program! ---");
Java OrderedPairDriver.java
public class OrderedPairDriver{
    public static void main(String[] args){
        System.out.println(
            "=== Ordered Pair Driver ===\n");
        double x = 2.0;
        double y = f(x);   // y = 8.0 — captured
        System.out.println(
            "f(" + x + ") = " + y
        );

        System.out.println(
            "\n--- Creating the Origin ---");
        OrderedPair origin = new OrderedPair();
        System.out.println("origin = " + origin);

        System.out.println(
            "\n--- Creating a Point ---");
        OrderedPair ptA = new OrderedPair(x, y);
        System.out.println("ptA = " + ptA);
        ptA.setLabel("A");
        System.out.println("ptA = " + ptA);
        //System.out.println(ptA.absVal); // compile error: private
        System.out.println(
            "AbsVal ptA = " + ptA.getAbsVal());

        System.out.println(
            "\n--- Creating a Transposed Point ---");
        OrderedPair ptA_t = new OrderedPair(ptA);
        ptA_t.transpose();
        System.out.println(
            "ptA_transpose = " + ptA_t);
        System.out.println("AbsVal "
            + ptA_t.getLabel()
            + " = " + ptA_t.getAbsVal());

        System.out.println(
            "\n--- Thanks for using our program! ---");
    }//end main

    // Return function: return type declared in signature
    public static double f(double x) {
        System.out.println("...f...");
        double m = 3.0;
        double b = 2.0;
        return m * x + b;
    }//end f
}//end class
Python 3 ordered_pair_main.py
# Add OrderedPair.py as a second file in
# OnlineGDB, then run this file.
from OrderedPair import OrderedPair

# Return function — mirrors Java's static double f(x)
def f(x):
    print("...f...")
    m = 3.0
    b = 2.0
    return m * x + b
#end f

def main():
    print("=== Ordered Pair Main ===\n")

    x = 2.0
    y = f(x)   # y = 8.0 — captured
    print(f"f({x}) = {y}")

    print("\n--- Creating the Origin ---")
    origin = OrderedPair()
    print("origin =", origin)

    print("\n--- Creating a Point ---")
    ptA = OrderedPair(x, y)
    print("ptA =", ptA)
    ptA.set_label("A")
    print("ptA =", ptA)
    #print(ptA._abs_val)  # accessible but breaks encapsulation
    print("AbsVal ptA =", ptA.get_abs_val())

    print("\n--- Creating a Transposed Point ---")
    ptA_t = OrderedPair.from_ordered_pair(ptA)
    ptA_t.transpose()
    print("ptA_transpose =", ptA_t)
    print("AbsVal", ptA_t.get_label(),
          "=", ptA_t.get_abs_val())

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

if __name__ == "__main__":
    main()

The Class Definitions

Same anatomy. Same logic. Study how each language declares fields, handles multiple constructors, and controls access to private state.

JavaScript OrderedPair.js
// One constructor; instanceof detects which
// "constructor path" the caller intends.
// Underscore = private by convention only.

class OrderedPair {

    constructor(xOrOrig, y) {

        if (xOrOrig instanceof OrderedPair) {
            // ── copy constructor path ──────────
            console.log("...copy constructor...");
            this._x      = xOrOrig._x;
            this._y      = xOrOrig._y;
            this._absVal = this._computeAbsVal();
            this._label  = xOrOrig._label
                           + "_transpose";

        } else if (xOrOrig !== undefined
                   && y !== undefined) {
            // ── two-parameter path ─────────────
            console.log("...two parameter constructor...");
            this._x      = xOrOrig;
            this._y      = y;
            this._absVal = this._computeAbsVal();
            this._label  = "P";

        } else {
            // ── default path ───────────────────
            console.log("...default constructor...");
            this._x      = 0;
            this._y      = 0;
            this._absVal = this._computeAbsVal();
            this._label  = "O";
        }
    }

    toString() {
        return this._label
            + "(" + this._x + ", " + 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;
    }

}//end class OrderedPair
Java OrderedPair.java
//(P)erhaps (C)lown (N)onsense (I)s (C)onstructive
//(O)nly (T)oward (G)etting (S)ettlers (U)nderwear
public class OrderedPair {
    // instance variables — private: enforced by compiler
    private double x;
    private double y;
    private double absVal;
    private String label;

    // default constructor
    public OrderedPair(){
        System.out.println("...default constructor...");
        x = 0;
        y = 0;
        absVal = computeAbsVal();
        label = "O";
    }//default constructor

    // two-parameter constructor
    // this.x disambiguates field from parameter
    public OrderedPair(double x, double y){
        System.out.println(
            "...two parameter constructor...");
        this.x = x;
        this.y = y;
        absVal = computeAbsVal();
        label = "P";
    }//two-parameter constructor

    // copy constructor
    public OrderedPair(OrderedPair orig){
        System.out.println("...copy constructor...");
        x = orig.x;
        y = orig.y;
        absVal = computeAbsVal();
        label = orig.label + "_transpose";
    }//end copy constructor

    // override toString
    public String toString(){
        String temp = label + "(";
        temp += x + ", " + y + ")";
        return temp;
    }//end toString

    // getters (accessors)
    public double getAbsVal(){ return absVal; }
    public String getLabel() { return label;  }

    // setter (mutator)
    public void setLabel(String lbl){
        System.out.println("...setLabel...");
        //dwr!
        label = lbl;
    }//end setLabel

    // utility methods
    public double computeAbsVal(){
        System.out.println("...computeAbsVal...");
        return Math.sqrt(
            Math.pow(x, 2) + Math.pow(y, 2));
    }//end computeAbsVal

    public void transpose(){
        System.out.println("...transpose...");
        // classic swap
        double temp = y;
        y = x;
        x = temp;
    }//end transpose

}//end class OrderedPair
Python 3 OrderedPair.py
# One __init__; @classmethod is the copy
# constructor. 'self' is explicit everywhere.
import math

class OrderedPair:

    def __init__(self, x=0.0, y=0.0):
        """Default + two-parameter constructor."""
        print("...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):
        """Python's substitute for the copy constructor.
        @classmethod receives the class as 'cls';
        delegates to __init__ for base setup.
        """
        print("...copy constructor (classmethod)...")
        instance        = cls(orig._x, orig._y)
        instance._label = orig._label + "_transpose"
        return instance

    # __str__ = Python's toString()
    def __str__(self):
        return (self._label
            + "(" + str(self._x)
            + ", " + str(self._y) + ")")

    # getters (accessors)
    def get_abs_val(self): return self._abs_val
    def get_label(self):   return self._label

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

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

    def transpose(self):
        print("...transpose...")
        # Pythonic swap — no temp variable needed
        self._x, self._y = self._y, self._x

#end class OrderedPair

Why Does Each Version Look Different?

The guiding question for every Cross Training page: “What does each version reveal about the language’s personality?”

JavaScript

One constructor, all three paths. JavaScript allows exactly one constructor per class. Java allows as many as you want, distinguished by their parameter signatures — the compiler picks the right one at compile time. JavaScript makes that choice at runtime instead, using instanceof to detect what kind of argument was passed. The logic is explicit and readable, but it puts the branching burden on you as the programmer.

Private by convention, not enforcement. The underscore prefix on _x and _absVal is a team agreement, not a language rule. Nothing stops a caller from writing ptA._absVal. JavaScript ES2022 introduced true private fields with the # prefix (#x, #absVal), which the engine does enforce — but browser compatibility varies and they are not used here. The key lesson: JavaScript trusts the programmer. Java does not.

toString() is implicit. When you write "origin = " + origin, JavaScript automatically calls toString() on the object. This is identical to Java’s behavior and one of the few places where the two languages feel exactly the same.

Java

Constructor overloading. Java’s three public OrderedPair(...) signatures are completely separate methods. The compiler resolves which one to call based on the argument types at compile time — no branching logic needed at runtime. This is overloading: same name, different signatures. It is one of Java’s most powerful (and most tested) design features.

private is enforced. The commented-out line //System.out.println(ptA.absVal); is not just a style suggestion — it is a compile error. private double absVal means the compiler actively prevents any class other than OrderedPair from reading that field. Encapsulation is not a convention in Java; it is a contract the compiler guarantees.

this.x = x disambiguation. In the two-parameter constructor, the parameter and the field share the same name. this.x refers unambiguously to the field; plain x refers to the parameter. This pattern appears constantly in Java constructors and is worth recognizing on sight.

Python 3

__init__ with default parameters. Python’s single __init__ handles both the no-arg and two-arg cases through default parameter values: def __init__(self, x=0.0, y=0.0). No branching needed. For the copy constructor, Python uses a @classmethod factory — a method that receives the class itself (as cls) rather than an instance, and returns a freshly constructed object. This is the Pythonic substitute for Java’s overloaded copy constructor.

self is explicit everywhere. Every instance method must list self as its first parameter. Java and JavaScript have implicit this; Python makes the receiver visible. This is not boilerplate — it is a deliberate design decision. Python’s philosophy: explicit is better than implicit. self also appears in every field reference: self._x, self._abs_val. There is no ambiguity about what x refers to — it always refers to the local scope.

Pythonic swap. self._x, self._y = self._y, self._x swaps two values in one line with no temporary variable. Java and JavaScript both require the classic temp = y; y = x; x = temp pattern. Python evaluates the right side completely before any assignment begins — tuple packing and unpacking does the rest. One line. Right intention.