You need a Point class with x and y. Writing constructor, getters, equals, hashCode, and toString is tedious. Records do all this in one line: record Point(int x, int y) {}. Immutable data carriers with no boilerplate.

Basic record

Define a data class in one line.

pointX
BasicRecord.java
Replay: real traced execution (multi-file project)
// Basic record definition and usage
// Concept: record - immutable data carrier

// Define a record
record Point(int x, int y) {
}

public class BasicRecord {
    public static void main(String[] args) {
        // Create record instance
        int pointX = 10;
        Point p1 = new Point(pointX, 20);

        // Access fields via accessor methods
        System.out.println("x: " + p1.x());
        System.out.println("y: " + p1.y());


        // Automatic toString()
        System.out.println("Point: " + p1);

        // Create another instance
        Point p2 = new Point(10, 20);
        Point p3 = new Point(15, 25);

        // Automatic equals()
        System.out.println("\np1 equals p2: " + p1.equals(p2));
        System.out.println("p1 equals p3: " + p1.equals(p3));

        // Use == for reference comparison
        System.out.println("p1 == p2: " + (p1 == p2));

        // More record examples
        record Person(String name, int age) {}

        Person alice = new Person("Alice", 30);
        System.out.println("\nPerson: " + alice);
        System.out.println("Name: " + alice.name());
        System.out.println("Age: " + alice.age());

    }
}
// Basic record definition and usage
// Concept: record - immutable data carrier

// Define a record
record Point(int x, int y) {
}

public class BasicRecord {
    public static void main(String[] args) {
        // Create record instance
        int pointX = 5;
        Point p1 = new Point(pointX, 20);

        // Access fields via accessor methods
        System.out.println("x: " + p1.x());
        System.out.println("y: " + p1.y());


        // Automatic toString()
        System.out.println("Point: " + p1);

        // Create another instance
        Point p2 = new Point(10, 20);
        Point p3 = new Point(15, 25);

        // Automatic equals()
        System.out.println("\np1 equals p2: " + p1.equals(p2));
        System.out.println("p1 equals p3: " + p1.equals(p3));

        // Use == for reference comparison
        System.out.println("p1 == p2: " + (p1 == p2));

        // More record examples
        record Person(String name, int age) {}

        Person alice = new Person("Alice", 30);
        System.out.println("\nPerson: " + alice);
        System.out.println("Name: " + alice.name());
        System.out.println("Age: " + alice.age());

    }
}
// Basic record definition and usage
// Concept: record - immutable data carrier

// Define a record
record Point(int x, int y) {
}

public class BasicRecord {
    public static void main(String[] args) {
        // Create record instance
        int pointX = 100;
        Point p1 = new Point(pointX, 20);

        // Access fields via accessor methods
        System.out.println("x: " + p1.x());
        System.out.println("y: " + p1.y());


        // Automatic toString()
        System.out.println("Point: " + p1);

        // Create another instance
        Point p2 = new Point(10, 20);
        Point p3 = new Point(15, 25);

        // Automatic equals()
        System.out.println("\np1 equals p2: " + p1.equals(p2));
        System.out.println("p1 equals p3: " + p1.equals(p3));

        // Use == for reference comparison
        System.out.println("p1 == p2: " + (p1 == p2));

        // More record examples
        record Person(String name, int age) {}

        Person alice = new Person("Alice", 30);
        System.out.println("\nPerson: " + alice);
        System.out.println("Name: " + alice.name());
        System.out.println("Age: " + alice.age());

    }
}
  1. pointX ← 10, p1 ← Point[x=10, y=20], p2 ← Point[x=10, y=20], p3 ← Point[x=15, y=25]

    8public class BasicRecord {9    public static void main(String[] args) {10        // Create record instance11        int pointX→ 10 = 10;12        //@pointX=10, 5, 10013        Point p1→ Point[x=10, y=20] = new Point(pointX, 20);1415        // Access fields via accessor methods16        System.out.println("x: " + p1.x());17        System.out.println("y: " + p1.y());18        19        //@help h120        // record Point(int x, int y) creates:21        // - private final int x, y fields22        // - public int x(), int y() accessors23        // - constructor Point(int x, int y)24        // - equals(), hashCode(), toString()25        //@end26        27        // Automatic toString()28        System.out.println("Point: " + p1Point[x=10, y=20]);29        30        // Create another instance31        Point p2→ Point[x=10, y=20] = new Point(10, 20);32        Point p3→ Point[x=15, y=25] = new Point(15, 25);33        34        // Automatic equals()35        System.out.println("\np1 equals p2: " + p1.equals(p2Point[x=10, y=20]));36        System.out.println("p1 equals p3: " + p1.equals(p3Point[x=15, y=25]));37        38        // Use == for reference comparison39        System.out.println("p1 == p2: " + (p1Point[x=10, y=20] == p2Point[x=10, y=20]));40        41        // More record examples42        record Person(String name, int age) {}43        44        Person alice = new Person("Alice", 30);45        System.out.println("\nPerson: " + alicePerson[name=Alice, age=30]);46        System.out.println("Name: " + alice.name());47        System.out.println("Age: " + alice.age());
    outputx: 10
    y: 20
    Point: Point[x=10, y=20]
    
    p1 equals p2: true
    p1 equals p3: false
    p1 == p2: false
    
    Person: Person[name=Alice, age=30]
    Name: Alice
    Age: 30
  1. pointX ← 5, p1 ← Point[x=5, y=20], p2 ← Point[x=10, y=20], p3 ← Point[x=15, y=25]

    8public class BasicRecord {9    public static void main(String[] args) {10        // Create record instance11        int pointX→ 5 = 5;12        Point p1→ Point[x=5, y=20] = new Point(pointX, 20);1314        // Access fields via accessor methods15        System.out.println("x: " + p1.x());16        System.out.println("y: " + p1.y());17        18        19        // Automatic toString()20        System.out.println("Point: " + p1Point[x=5, y=20]);21        22        // Create another instance23        Point p2→ Point[x=10, y=20] = new Point(10, 20);24        Point p3→ Point[x=15, y=25] = new Point(15, 25);25        26        // Automatic equals()27        System.out.println("\np1 equals p2: " + p1.equals(p2Point[x=10, y=20]));28        System.out.println("p1 equals p3: " + p1.equals(p3Point[x=15, y=25]));29        30        // Use == for reference comparison31        System.out.println("p1 == p2: " + (p1Point[x=5, y=20] == p2Point[x=10, y=20]));32        33        // More record examples34        record Person(String name, int age) {}35        36        Person alice = new Person("Alice", 30);37        System.out.println("\nPerson: " + alicePerson[name=Alice, age=30]);38        System.out.println("Name: " + alice.name());39        System.out.println("Age: " + alice.age());
    outputx: 5
    y: 20
    Point: Point[x=5, y=20]
    
    p1 equals p2: false
    p1 equals p3: false
    p1 == p2: false
    
    Person: Person[name=Alice, age=30]
    Name: Alice
    Age: 30
  1. pointX ← 100, p1 ← Point[x=100, y=20], p2 ← Point[x=10, y=20]

    8public class BasicRecord {9    public static void main(String[] args) {10        // Create record instance11        int pointX→ 100 = 100;12        Point p1→ Point[x=100, y=20] = new Point(pointX, 20);1314        // Access fields via accessor methods15        System.out.println("x: " + p1.x());16        System.out.println("y: " + p1.y());17        18        19        // Automatic toString()20        System.out.println("Point: " + p1Point[x=100, y=20]);21        22        // Create another instance23        Point p2→ Point[x=10, y=20] = new Point(10, 20);24        Point p3→ Point[x=15, y=25] = new Point(15, 25);25        26        // Automatic equals()27        System.out.println("\np1 equals p2: " + p1.equals(p2Point[x=10, y=20]));28        System.out.println("p1 equals p3: " + p1.equals(p3Point[x=15, y=25]));29        30        // Use == for reference comparison31        System.out.println("p1 == p2: " + (p1Point[x=100, y=20] == p2Point[x=10, y=20]));32        33        // More record examples34        record Person(String name, int age) {}35        36        Person alice = new Person("Alice", 30);37        System.out.println("\nPerson: " + alicePerson[name=Alice, age=30]);38        System.out.println("Name: " + alice.name());39        System.out.println("Age: " + alice.age());
    outputx: 100
    y: 20
    Point: Point[x=100, y=20]
    
    p1 equals p2: false
    p1 equals p3: false
    p1 == p2: false
    
    Person: Person[name=Alice, age=30]
    Name: Alice
    Age: 30

record Name(Type field1, Type field2) {} - complete data class.

record Immutable data carrier. Auto-generates constructor, accessors, equals, hashCode, toString.

Automatic methods

What records give you for free.

AutomaticMethods.java
Replay: real traced execution (multi-file project)
// Automatic methods in records
// Concept: automatic methods - equals, hashCode, toString

record Book(String title, String author, int year) {
}

public class AutomaticMethods {
    public static void main(String[] args) {
        // Create book instances
        Book b1 = new Book("1984", "Orwell", 1949);
        Book b2 = new Book("1984", "Orwell", 1949);
        Book b3 = new Book("Dune", "Herbert", 1965);

        // toString() - readable representation
        System.out.println("Book 1: " + b1);
        System.out.println("Book 2: " + b2);
        System.out.println("Book 3: " + b3);

        // equals() - value-based equality
        System.out.println("\nb1.equals(b2): " + b1.equals(b2));
        System.out.println("b1.equals(b3): " + b1.equals(b3));


        // hashCode() - consistent with equals
        System.out.println("\nHash codes:");
        System.out.println("b1: " + b1.hashCode());
        System.out.println("b2: " + b2.hashCode());
        System.out.println("b3: " + b3.hashCode());

        System.out.println("\nSame hash? " + (b1.hashCode() == b2.hashCode()));

        // Use in collections
        java.util.Set<Book> books = new java.util.HashSet<>();
        books.add(b1);
        books.add(b2);  // Won't add (equals b1)
        books.add(b3);

        System.out.println("\nUnique books in set: " + books.size());

        // Use as map key
        java.util.Map<Book, Integer> stock = new java.util.HashMap<>();
        stock.put(b1, 10);
        stock.put(b2, 5);  // Updates b1's value (same key)
        stock.put(b3, 7);

        System.out.println("\nStock levels:");
        for (var entry : stock.entrySet()) {
            System.out.println("  " + entry.getKey().title() + ": " + entry.getValue());
        }

        // Compare different types
        record Movie(String title, String director, int year) {}

        Movie m1 = new Movie("1984", "Radford", 1984);

        // Different types, even with same values
        System.out.println("\nBook equals Movie: " + b1.equals(m1));
    }
}
  1. b1 ← Book[title=1984, author=Orwell, year=1949], b2 ← Book[title=1984, author=Orwell, year=1949]

    7public class AutomaticMethods {8    public static void main(String[] args) {9        // Create book instances10        Book b1→ Book[title=1984, author=Orwell, year=1949] = new Book("1984", "Orwell", 1949);11        Book b2→ Book[title=1984, author=Orwell, year=1949] = new Book("1984", "Orwell", 1949);12        Book b3→ Book[title=Dune, author=Herbert, year=1965] = new Book("Dune", "Herbert", 1965);13        14        // toString() - readable representation15        System.out.println("Book 1: " + b1Book[title=1984, author=Orwell, year=1949]);16        System.out.println("Book 2: " + b2Book[title=1984, author=Orwell, year=1949]);17        System.out.println("Book 3: " + b3Book[title=Dune, author=Herbert, year=1965]);18        19        // equals() - value-based equality20        System.out.println("\nb1.equals(b2): " + b1.equals(b2Book[title=1984, author=Orwell, year=1949]));21        System.out.println("b1.equals(b3): " + b1.equals(b3Book[title=Dune, author=Herbert, year=1965]));22        23        //@help h124        // Records compare by value, not reference25        // All fields must match for equality26        // toString() shows all field values27        //@end28        29        // hashCode() - consistent with equals30        System.out.println("\nHash codes:");31        System.out.println("b1: " + b1.hashCode());32        System.out.println("b2: " + b2.hashCode());33        System.out.println("b3: " + b3.hashCode());34        35        System.out.println("\nSame hash? " + (b1.hashCode() == b2.hashCode()));36        37        // Use in collections38        java.util.Set<Book> books→ [] = new java.util.HashSet<>();39        books.add(b1Book[title=1984, author=Orwell, year=1949]);40        books.add(b2Book[title=1984, author=Orwell, year=1949]);  // Won't add (equals b1)41        books.add(b3Book[title=Dune, author=Herbert, year=1965]);42        43        System.out.println("\nUnique books in set: " + books.size());44        45        // Use as map key46        java.util.Map<Book, Integer> stock→ {} = new java.util.HashMap<>();47        stock.put(b1Book[title=1984, author=Orwell, year=1949], 10);48        stock.put(b2Book[title=1984, author=Orwell, year=1949], 5);  // Updates b1's value (same key)49        stock.put(b3Book[title=Dune, author=Herbert, year=1965], 7);50        51        System.out.println("\nStock levels:");52        for (var entry : stock.entrySet()) {
    outputBook 1: Book[title=1984, author=Orwell, year=1949]
    Book 2: Book[title=1984, author=Orwell, year=1949]
    Book 3: Book[title=Dune, author=Herbert, year=1965]
    
    b1.equals(b2): true
    b1.equals(b3): false
    
    Hash codes:
    b1: 1932274320
    b2: 1932274320
    b3: 1492444379
    
    Same hash? true
    
    Unique books in set: 2
    
    Stock levels:
  2. for (var entry : stock.entrySet())

    pass 1 of 2
    51System.out.println("\nStock levels:");52for (var entry : stock.entrySet()) {53    System.out.println("  " + entry.getKey().title() + ": " + entry.getValue());54}
    output  1984: 5
  3. for (var entry : stock.entrySet())

    pass 2 of 2
    51System.out.println("\nStock levels:");52for (var entry : stock.entrySet()) {53    System.out.println("  " + entry.getKey().title() + ": " + entry.getValue());54}
    output  Dune: 7
  4. m1 ← Movie[title=1984, director=Radford, year=1984]

    59    Movie m1→ Movie[title=1984, director=Radford, year=1984] = new Movie("1984", "Radford", 1984);60    61    // Different types, even with same values62    System.out.println("\nBook equals Movie: " + b1.equals(m1Movie[title=1984, director=Radford, year=1984]));63}
    output
    Book equals Movie: false

Accessor methods: point.x(). Plus equals, hashCode, toString.

Compact constructor

Validate fields in a compact constructor.

t1
CompactConstructor.java
Replay: real traced execution (multi-file project)
// Compact constructor for validation
// Concept: compact constructor - validate record creation

record Temperature(double celsius) {
    // Compact constructor for validation
    public Temperature {
        if (celsius < -273.15) {
            throw new IllegalArgumentException(
                "Temperature below absolute zero: " + celsius);
        }
    }

    // Add computed methods
    public double fahrenheit() {
        return celsius * 9.0 / 5.0 + 32;
    }

    public double kelvin() {
        return celsius + 273.15;
    }
}

record Range(int min, int max) {
    // Compact constructor with normalization
    public Range {
        if (min > max) {
            // Swap if needed
            int temp = min;
            min = max;
            max = temp;
        }
    }

    public boolean contains(int value) {
        return value >= min && value <= max;
    }

    public int size() {
        return max - min + 1;
    }
}

public class CompactConstructor {
    public static void main(String[] args) {
        // Create temperature with validation
        Temperature t1 = new Temperature(25.0);
        System.out.println("Temperature: " + t1.celsius() + "°C");
        System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");
        System.out.println("Kelvin: " + t1.kelvin() + "K");


        // Validation prevents invalid state
        try {
            Temperature invalid = new Temperature(-300);
            System.out.println("Created: " + invalid);
        } catch (IllegalArgumentException e) {
            System.out.println("\nError: " + e.getMessage());
        }

        // Range with auto-correction
        Range r1 = new Range(1, 10);
        Range r2 = new Range(10, 1);  // Will be swapped

        System.out.println("\nr1: " + r1);
        System.out.println("r2: " + r2);
        System.out.println("r1 equals r2: " + r1.equals(r2));

        // Use range methods
        Range range = new Range(5, 15);

        System.out.println("\nRange: " + range);
        System.out.println("Size: " + range.size());
        System.out.println("Contains 10: " + range.contains(10));
        System.out.println("Contains 20: " + range.contains(20));

        // Multiple validations
        record Email(String address) {
            public Email {
                if (address == null || address.isEmpty()) {
                    throw new IllegalArgumentException("Email cannot be empty");
                }
                if (!address.contains("@")) {
                    throw new IllegalArgumentException("Invalid email format");
                }
                // Normalize to lowercase
                address = address.toLowerCase();
            }
        }

        Email e1 = new Email("Alice@Example.COM");
        Email e2 = new Email("alice@example.com");

        System.out.println("\ne1: " + e1.address());
        System.out.println("e2: " + e2.address());
        System.out.println("Equal: " + e1.equals(e2));

    }
}
// Compact constructor for validation
// Concept: compact constructor - validate record creation

record Temperature(double celsius) {
    // Compact constructor for validation
    public Temperature {
        if (celsius < -273.15) {
            throw new IllegalArgumentException(
                "Temperature below absolute zero: " + celsius);
        }
    }

    // Add computed methods
    public double fahrenheit() {
        return celsius * 9.0 / 5.0 + 32;
    }

    public double kelvin() {
        return celsius + 273.15;
    }
}

record Range(int min, int max) {
    // Compact constructor with normalization
    public Range {
        if (min > max) {
            // Swap if needed
            int temp = min;
            min = max;
            max = temp;
        }
    }

    public boolean contains(int value) {
        return value >= min && value <= max;
    }

    public int size() {
        return max - min + 1;
    }
}

public class CompactConstructor {
    public static void main(String[] args) {
        // Create temperature with validation
        Temperature t1 = new Temperature(0);
        System.out.println("Temperature: " + t1.celsius() + "°C");
        System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");
        System.out.println("Kelvin: " + t1.kelvin() + "K");


        // Validation prevents invalid state
        try {
            Temperature invalid = new Temperature(-300);
            System.out.println("Created: " + invalid);
        } catch (IllegalArgumentException e) {
            System.out.println("\nError: " + e.getMessage());
        }

        // Range with auto-correction
        Range r1 = new Range(1, 10);
        Range r2 = new Range(10, 1);  // Will be swapped

        System.out.println("\nr1: " + r1);
        System.out.println("r2: " + r2);
        System.out.println("r1 equals r2: " + r1.equals(r2));

        // Use range methods
        Range range = new Range(5, 15);

        System.out.println("\nRange: " + range);
        System.out.println("Size: " + range.size());
        System.out.println("Contains 10: " + range.contains(10));
        System.out.println("Contains 20: " + range.contains(20));

        // Multiple validations
        record Email(String address) {
            public Email {
                if (address == null || address.isEmpty()) {
                    throw new IllegalArgumentException("Email cannot be empty");
                }
                if (!address.contains("@")) {
                    throw new IllegalArgumentException("Invalid email format");
                }
                // Normalize to lowercase
                address = address.toLowerCase();
            }
        }

        Email e1 = new Email("Alice@Example.COM");
        Email e2 = new Email("alice@example.com");

        System.out.println("\ne1: " + e1.address());
        System.out.println("e2: " + e2.address());
        System.out.println("Equal: " + e1.equals(e2));

    }
}
// Compact constructor for validation
// Concept: compact constructor - validate record creation

record Temperature(double celsius) {
    // Compact constructor for validation
    public Temperature {
        if (celsius < -273.15) {
            throw new IllegalArgumentException(
                "Temperature below absolute zero: " + celsius);
        }
    }

    // Add computed methods
    public double fahrenheit() {
        return celsius * 9.0 / 5.0 + 32;
    }

    public double kelvin() {
        return celsius + 273.15;
    }
}

record Range(int min, int max) {
    // Compact constructor with normalization
    public Range {
        if (min > max) {
            // Swap if needed
            int temp = min;
            min = max;
            max = temp;
        }
    }

    public boolean contains(int value) {
        return value >= min && value <= max;
    }

    public int size() {
        return max - min + 1;
    }
}

public class CompactConstructor {
    public static void main(String[] args) {
        // Create temperature with validation
        Temperature t1 = new Temperature(100);
        System.out.println("Temperature: " + t1.celsius() + "°C");
        System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");
        System.out.println("Kelvin: " + t1.kelvin() + "K");


        // Validation prevents invalid state
        try {
            Temperature invalid = new Temperature(-300);
            System.out.println("Created: " + invalid);
        } catch (IllegalArgumentException e) {
            System.out.println("\nError: " + e.getMessage());
        }

        // Range with auto-correction
        Range r1 = new Range(1, 10);
        Range r2 = new Range(10, 1);  // Will be swapped

        System.out.println("\nr1: " + r1);
        System.out.println("r2: " + r2);
        System.out.println("r1 equals r2: " + r1.equals(r2));

        // Use range methods
        Range range = new Range(5, 15);

        System.out.println("\nRange: " + range);
        System.out.println("Size: " + range.size());
        System.out.println("Contains 10: " + range.contains(10));
        System.out.println("Contains 20: " + range.contains(20));

        // Multiple validations
        record Email(String address) {
            public Email {
                if (address == null || address.isEmpty()) {
                    throw new IllegalArgumentException("Email cannot be empty");
                }
                if (!address.contains("@")) {
                    throw new IllegalArgumentException("Invalid email format");
                }
                // Normalize to lowercase
                address = address.toLowerCase();
            }
        }

        Email e1 = new Email("Alice@Example.COM");
        Email e2 = new Email("alice@example.com");

        System.out.println("\ne1: " + e1.address());
        System.out.println("e2: " + e2.address());
        System.out.println("Equal: " + e1.equals(e2));

    }
}
  1. public static void main(String[] args)

    43public class CompactConstructor {44    public static void main(String[] args) {45        // Create temperature with validation46        Temperature t1 = new Temperature(25.0);47        //@t1=new Temperature(25.0), new Temperature(0), new Temperature(100)
  2. t1 ← Temperature[celsius=25.0]

    45// Create temperature with validation46Temperature t1→ Temperature[celsius=25.0] = new Temperature(25.0);47//@t1=new Temperature(25.0), new Temperature(0), new Temperature(100)48System.out.println("Temperature: " + t1.celsius() + "°C");49System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");50System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputTemperature: 25.0°C
  3. public double fahrenheit()

    13// Add computed methods14public double fahrenheit() {15    return celsius25.0 * 9.0 / 5.0 + 32;16}
  4. System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");

    48System.out.println("Temperature: " + t1.celsius() + "°C");49System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");50System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputFahrenheit: 77.0°F
  5. public double kelvin()

    18public double kelvin() {19    return celsius25.0 + 273.15;20}
  6. System.out.println("Kelvin: " + t1.kelvin() + "K");

    49System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");50System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputKelvin: 298.15K
  7. if (celsius < -273.15)

    6public Temperature {7    if (celsius-300.0 < -273.15) {8        throw new IllegalArgumentException(9            "Temperature below absolute zero: " + celsius);10    }
  8. catch (IllegalArgumentException e)

    62    System.out.println("Created: " + invalid);63} catch (IllegalArgumentException ejava.lang.IllegalArgumentException: Temperature below absolute zero: -300.0) {64    System.out.println("\nError: " + e.getMessage());65}
    output
    Error: Temperature below absolute zero: -300.0
  9. Range r1 = new Range(1, 10);

    67// Range with auto-correction68Range r1 = new Range(1, 10);69Range r2 = new Range(10, 1);  // Will be swapped
  10. public Range

    pass 1 of 3
    24// Compact constructor with normalization25public Range {26    if (min > max) {
    All 3 passes — pass 1 is the card above
    passtempminmax
    1
    21010 11 10
    3
  11. r1 ← Range[min=1, max=10]

    67// Range with auto-correction68Range r1→ Range[min=1, max=10] = new Range(1, 10);69Range r2 = new Range(10, 1);  // Will be swapped
  12. temp ← 10, min ← 1, max ← 10

    25public Range {26    if (min10 > max1) {27        // Swap if needed28        int temp→ 10 = min;29        min→ 1 = max1;30        max→ 10 = temp10;31    }
  13. r2 ← Range[min=1, max=10]

    68Range r1 = new Range(1, 10);69Range r2→ Range[min=1, max=10] = new Range(10, 1);  // Will be swapped7071System.out.println("\nr1: " + r1Range[min=1, max=10]);72System.out.println("r2: " + r2Range[min=1, max=10]);73System.out.println("r1 equals r2: " + r1.equals(r2Range[min=1, max=10]));7475// Use range methods76Range range = new Range(5, 15);
    output
    r1: Range[min=1, max=10]
    r2: Range[min=1, max=10]
    r1 equals r2: true
  14. range ← Range[min=5, max=15]

    75// Use range methods76Range range→ Range[min=5, max=15] = new Range(5, 15);7778System.out.println("\nRange: " + rangeRange[min=5, max=15]);79System.out.println("Size: " + range.size());80System.out.println("Contains 10: " + range.contains(10));
    output
    Range: Range[min=5, max=15]
  15. public int size()

    38public int size() {39    return max15 - min5 + 1;40}
  16. System.out.println("Size: " + range.size());

    78System.out.println("\nRange: " + range);79System.out.println("Size: " + range.size());80System.out.println("Contains 10: " + range.contains(10));81System.out.println("Contains 20: " + range.contains(20));
    outputSize: 11
  17. public boolean contains(int value)

    pass 1 of 2
    34public boolean contains(int value10) {35    return value10 >= min5 && value <= max15;36}
  18. System.out.println("Contains 10: " + range.contains(10));

    79System.out.println("Size: " + range.size());80System.out.println("Contains 10: " + range.contains(10));81System.out.println("Contains 20: " + range.contains(20));
    outputContains 10: true
  19. public boolean contains(int value)

    pass 2 of 2
    34public boolean contains(int value20) {35    return value20 >= min5 && value <= max15;36}
  20. System.out.println("Contains 20: " + range.contains(20));

    80System.out.println("Contains 10: " + range.contains(10));81System.out.println("Contains 20: " + range.contains(20));8283// Multiple validations84record Email(String address) {85    public Email {86        if (address == null || address.isEmpty()) {87            throw new IllegalArgumentException("Email cannot be empty");88        }89        if (!address.contains("@")) {90            throw new IllegalArgumentException("Invalid email format");91        }92        // Normalize to lowercase93        address = address.toLowerCase();94    }95}9697Email e1 = new Email("Alice@Example.COM");98Email e2 = new Email("alice@example.com");
    outputContains 20: false
  21. address ← alice@example.com

    pass 1 of 2
    84record Email(String address) {85    public Email {86        if (address == null || address.isEmpty()) {87            throw new IllegalArgumentException("Email cannot be empty");88        }89        if (!address.contains("@")) {90            throw new IllegalArgumentException("Invalid email format");91        }92        // Normalize to lowercase93        address→ alice@example.com = address.toLowerCase();94    }
  22. e1 ← Email[address=alice@example.com]

    97Email e1→ Email[address=alice@example.com] = new Email("Alice@Example.COM");98Email e2 = new Email("alice@example.com");
  23. address ← alice@example.com

    pass 2 of 2
    84record Email(String address) {85    public Email {86        if (address == null || address.isEmpty()) {87            throw new IllegalArgumentException("Email cannot be empty");88        }89        if (!address.contains("@")) {90            throw new IllegalArgumentException("Invalid email format");91        }92        // Normalize to lowercase93        address→ alice@example.com = address.toLowerCase();94    }
  24. e2 ← Email[address=alice@example.com]

    97Email e1 = new Email("Alice@Example.COM");98Email e2→ Email[address=alice@example.com] = new Email("alice@example.com");99100System.out.println("\ne1: " + e1.address());101System.out.println("e2: " + e2.address());102System.out.println("Equal: " + e1.equals(e2Email[address=alice@example.com]));
    output
    e1: alice@example.com
    e2: alice@example.com
    Equal: true
  1. public static void main(String[] args)

    43public class CompactConstructor {44    public static void main(String[] args) {45        // Create temperature with validation46        Temperature t1 = new Temperature(0);47        System.out.println("Temperature: " + t1.celsius() + "°C");
  2. t1 ← Temperature[celsius=0.0]

    45// Create temperature with validation46Temperature t1→ Temperature[celsius=0.0] = new Temperature(0);47System.out.println("Temperature: " + t1.celsius() + "°C");48System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");49System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputTemperature: 0.0°C
  3. public double fahrenheit()

    13// Add computed methods14public double fahrenheit() {15    return celsius0.0 * 9.0 / 5.0 + 32;16}
  4. System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");

    47System.out.println("Temperature: " + t1.celsius() + "°C");48System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");49System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputFahrenheit: 32.0°F
  5. public double kelvin()

    18public double kelvin() {19    return celsius0.0 + 273.15;20}
  6. System.out.println("Kelvin: " + t1.kelvin() + "K");

    48System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");49System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputKelvin: 273.15K
  7. if (celsius < -273.15)

    6public Temperature {7    if (celsius-300.0 < -273.15) {8        throw new IllegalArgumentException(9            "Temperature below absolute zero: " + celsius);10    }
  8. catch (IllegalArgumentException e)

    55    System.out.println("Created: " + invalid);56} catch (IllegalArgumentException ejava.lang.IllegalArgumentException: Temperature below absolute zero: -300.0) {57    System.out.println("\nError: " + e.getMessage());58}
    output
    Error: Temperature below absolute zero: -300.0
  9. Range r1 = new Range(1, 10);

    60// Range with auto-correction61Range r1 = new Range(1, 10);62Range r2 = new Range(10, 1);  // Will be swapped
  10. public Range

    pass 1 of 3
    24// Compact constructor with normalization25public Range {26    if (min > max) {
    All 3 passes — pass 1 is the card above
    passtempminmax
    1
    21010 11 10
    3
  11. r1 ← Range[min=1, max=10]

    60// Range with auto-correction61Range r1→ Range[min=1, max=10] = new Range(1, 10);62Range r2 = new Range(10, 1);  // Will be swapped
  12. temp ← 10, min ← 1, max ← 10

    25public Range {26    if (min10 > max1) {27        // Swap if needed28        int temp→ 10 = min;29        min→ 1 = max1;30        max→ 10 = temp10;31    }
  13. r2 ← Range[min=1, max=10]

    61Range r1 = new Range(1, 10);62Range r2→ Range[min=1, max=10] = new Range(10, 1);  // Will be swapped6364System.out.println("\nr1: " + r1Range[min=1, max=10]);65System.out.println("r2: " + r2Range[min=1, max=10]);66System.out.println("r1 equals r2: " + r1.equals(r2Range[min=1, max=10]));6768// Use range methods69Range range = new Range(5, 15);
    output
    r1: Range[min=1, max=10]
    r2: Range[min=1, max=10]
    r1 equals r2: true
  14. range ← Range[min=5, max=15]

    68// Use range methods69Range range→ Range[min=5, max=15] = new Range(5, 15);7071System.out.println("\nRange: " + rangeRange[min=5, max=15]);72System.out.println("Size: " + range.size());73System.out.println("Contains 10: " + range.contains(10));
    output
    Range: Range[min=5, max=15]
  15. public int size()

    38public int size() {39    return max15 - min5 + 1;40}
  16. System.out.println("Size: " + range.size());

    71System.out.println("\nRange: " + range);72System.out.println("Size: " + range.size());73System.out.println("Contains 10: " + range.contains(10));74System.out.println("Contains 20: " + range.contains(20));
    outputSize: 11
  17. public boolean contains(int value)

    pass 1 of 2
    34public boolean contains(int value10) {35    return value10 >= min5 && value <= max15;36}
  18. System.out.println("Contains 10: " + range.contains(10));

    72System.out.println("Size: " + range.size());73System.out.println("Contains 10: " + range.contains(10));74System.out.println("Contains 20: " + range.contains(20));
    outputContains 10: true
  19. public boolean contains(int value)

    pass 2 of 2
    34public boolean contains(int value20) {35    return value20 >= min5 && value <= max15;36}
  20. System.out.println("Contains 20: " + range.contains(20));

    73System.out.println("Contains 10: " + range.contains(10));74System.out.println("Contains 20: " + range.contains(20));7576// Multiple validations77record Email(String address) {78    public Email {79        if (address == null || address.isEmpty()) {80            throw new IllegalArgumentException("Email cannot be empty");81        }82        if (!address.contains("@")) {83            throw new IllegalArgumentException("Invalid email format");84        }85        // Normalize to lowercase86        address = address.toLowerCase();87    }88}8990Email e1 = new Email("Alice@Example.COM");91Email e2 = new Email("alice@example.com");
    outputContains 20: false
  21. address ← alice@example.com

    pass 1 of 2
    77record Email(String address) {78    public Email {79        if (address == null || address.isEmpty()) {80            throw new IllegalArgumentException("Email cannot be empty");81        }82        if (!address.contains("@")) {83            throw new IllegalArgumentException("Invalid email format");84        }85        // Normalize to lowercase86        address→ alice@example.com = address.toLowerCase();87    }
  22. e1 ← Email[address=alice@example.com]

    90Email e1→ Email[address=alice@example.com] = new Email("Alice@Example.COM");91Email e2 = new Email("alice@example.com");
  23. address ← alice@example.com

    pass 2 of 2
    77record Email(String address) {78    public Email {79        if (address == null || address.isEmpty()) {80            throw new IllegalArgumentException("Email cannot be empty");81        }82        if (!address.contains("@")) {83            throw new IllegalArgumentException("Invalid email format");84        }85        // Normalize to lowercase86        address→ alice@example.com = address.toLowerCase();87    }
  24. e2 ← Email[address=alice@example.com]

    90Email e1 = new Email("Alice@Example.COM");91Email e2→ Email[address=alice@example.com] = new Email("alice@example.com");9293System.out.println("\ne1: " + e1.address());94System.out.println("e2: " + e2.address());95System.out.println("Equal: " + e1.equals(e2Email[address=alice@example.com]));
    output
    e1: alice@example.com
    e2: alice@example.com
    Equal: true
  1. public static void main(String[] args)

    43public class CompactConstructor {44    public static void main(String[] args) {45        // Create temperature with validation46        Temperature t1 = new Temperature(100);47        System.out.println("Temperature: " + t1.celsius() + "°C");
  2. t1 ← Temperature[celsius=100.0]

    45// Create temperature with validation46Temperature t1→ Temperature[celsius=100.0] = new Temperature(100);47System.out.println("Temperature: " + t1.celsius() + "°C");48System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");49System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputTemperature: 100.0°C
  3. public double fahrenheit()

    13// Add computed methods14public double fahrenheit() {15    return celsius100.0 * 9.0 / 5.0 + 32;16}
  4. System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");

    47System.out.println("Temperature: " + t1.celsius() + "°C");48System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");49System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputFahrenheit: 212.0°F
  5. public double kelvin()

    18public double kelvin() {19    return celsius100.0 + 273.15;20}
  6. System.out.println("Kelvin: " + t1.kelvin() + "K");

    48System.out.println("Fahrenheit: " + t1.fahrenheit() + "°F");49System.out.println("Kelvin: " + t1.kelvin() + "K");
    outputKelvin: 373.15K
  7. if (celsius < -273.15)

    6public Temperature {7    if (celsius-300.0 < -273.15) {8        throw new IllegalArgumentException(9            "Temperature below absolute zero: " + celsius);10    }
  8. catch (IllegalArgumentException e)

    55    System.out.println("Created: " + invalid);56} catch (IllegalArgumentException ejava.lang.IllegalArgumentException: Temperature below absolute zero: -300.0) {57    System.out.println("\nError: " + e.getMessage());58}
    output
    Error: Temperature below absolute zero: -300.0
  9. Range r1 = new Range(1, 10);

    60// Range with auto-correction61Range r1 = new Range(1, 10);62Range r2 = new Range(10, 1);  // Will be swapped
  10. public Range

    pass 1 of 3
    24// Compact constructor with normalization25public Range {26    if (min > max) {
    All 3 passes — pass 1 is the card above
    passtempminmax
    1
    21010 11 10
    3
  11. r1 ← Range[min=1, max=10]

    60// Range with auto-correction61Range r1→ Range[min=1, max=10] = new Range(1, 10);62Range r2 = new Range(10, 1);  // Will be swapped
  12. temp ← 10, min ← 1, max ← 10

    25public Range {26    if (min10 > max1) {27        // Swap if needed28        int temp→ 10 = min;29        min→ 1 = max1;30        max→ 10 = temp10;31    }
  13. r2 ← Range[min=1, max=10]

    61Range r1 = new Range(1, 10);62Range r2→ Range[min=1, max=10] = new Range(10, 1);  // Will be swapped6364System.out.println("\nr1: " + r1Range[min=1, max=10]);65System.out.println("r2: " + r2Range[min=1, max=10]);66System.out.println("r1 equals r2: " + r1.equals(r2Range[min=1, max=10]));6768// Use range methods69Range range = new Range(5, 15);
    output
    r1: Range[min=1, max=10]
    r2: Range[min=1, max=10]
    r1 equals r2: true
  14. range ← Range[min=5, max=15]

    68// Use range methods69Range range→ Range[min=5, max=15] = new Range(5, 15);7071System.out.println("\nRange: " + rangeRange[min=5, max=15]);72System.out.println("Size: " + range.size());73System.out.println("Contains 10: " + range.contains(10));
    output
    Range: Range[min=5, max=15]
  15. public int size()

    38public int size() {39    return max15 - min5 + 1;40}
  16. System.out.println("Size: " + range.size());

    71System.out.println("\nRange: " + range);72System.out.println("Size: " + range.size());73System.out.println("Contains 10: " + range.contains(10));74System.out.println("Contains 20: " + range.contains(20));
    outputSize: 11
  17. public boolean contains(int value)

    pass 1 of 2
    34public boolean contains(int value10) {35    return value10 >= min5 && value <= max15;36}
  18. System.out.println("Contains 10: " + range.contains(10));

    72System.out.println("Size: " + range.size());73System.out.println("Contains 10: " + range.contains(10));74System.out.println("Contains 20: " + range.contains(20));
    outputContains 10: true
  19. public boolean contains(int value)

    pass 2 of 2
    34public boolean contains(int value20) {35    return value20 >= min5 && value <= max15;36}
  20. System.out.println("Contains 20: " + range.contains(20));

    73System.out.println("Contains 10: " + range.contains(10));74System.out.println("Contains 20: " + range.contains(20));7576// Multiple validations77record Email(String address) {78    public Email {79        if (address == null || address.isEmpty()) {80            throw new IllegalArgumentException("Email cannot be empty");81        }82        if (!address.contains("@")) {83            throw new IllegalArgumentException("Invalid email format");84        }85        // Normalize to lowercase86        address = address.toLowerCase();87    }88}8990Email e1 = new Email("Alice@Example.COM");91Email e2 = new Email("alice@example.com");
    outputContains 20: false
  21. address ← alice@example.com

    pass 1 of 2
    77record Email(String address) {78    public Email {79        if (address == null || address.isEmpty()) {80            throw new IllegalArgumentException("Email cannot be empty");81        }82        if (!address.contains("@")) {83            throw new IllegalArgumentException("Invalid email format");84        }85        // Normalize to lowercase86        address→ alice@example.com = address.toLowerCase();87    }
  22. e1 ← Email[address=alice@example.com]

    90Email e1→ Email[address=alice@example.com] = new Email("Alice@Example.COM");91Email e2 = new Email("alice@example.com");
  23. address ← alice@example.com

    pass 2 of 2
    77record Email(String address) {78    public Email {79        if (address == null || address.isEmpty()) {80            throw new IllegalArgumentException("Email cannot be empty");81        }82        if (!address.contains("@")) {83            throw new IllegalArgumentException("Invalid email format");84        }85        // Normalize to lowercase86        address→ alice@example.com = address.toLowerCase();87    }
  24. e2 ← Email[address=alice@example.com]

    90Email e1 = new Email("Alice@Example.COM");91Email e2→ Email[address=alice@example.com] = new Email("alice@example.com");9293System.out.println("\ne1: " + e1.address());94System.out.println("e2: " + e2.address());95System.out.println("Equal: " + e1.equals(e2Email[address=alice@example.com]));
    output
    e1: alice@example.com
    e2: alice@example.com
    Equal: true

Constructor without parameters - validates/normalizes before assignment.

compact constructor `record Point(int x, int y) { Point { if (x < 0) throw...; } }` - validation.

Custom methods

Add your own methods to records.

c1
CustomMethods.java
Replay: real traced execution (multi-file project)
// Custom methods in records
// Concept: custom methods - add behavior to records

record Circle(double radius) {
    // Add custom methods
    public double area() {
        return Math.PI * radius * radius;
    }

    public double circumference() {
        return 2 * Math.PI * radius;
    }

    public double diameter() {
        return 2 * radius;
    }

    // Static factory method
    public static Circle fromDiameter(double diameter) {
        return new Circle(diameter / 2);
    }
}

record Money(double amount, String currency) {
    // Methods with logic
    public Money add(Money other) {
        if (!currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
        return new Money(amount + other.amount, currency);
    }

    public Money multiply(double factor) {
        return new Money(amount * factor, currency);
    }

    public String formatted() {
        return String.format("%.2f %s", amount, currency);
    }
}

public class CustomMethods {
    public static void main(String[] args) {
        // Use custom methods
        Circle c1 = new Circle(5.0);

        System.out.println("Circle: " + c1);
        System.out.println("Radius: " + c1.radius());
        System.out.printf("Area: %.2f%n", c1.area());
        System.out.printf("Circumference: %.2f%n", c1.circumference());
        System.out.printf("Diameter: %.2f%n", c1.diameter());


        // Use static factory
        Circle c2 = Circle.fromDiameter(20);
        System.out.println("\nFrom diameter 20: " + c2);
        System.out.printf("Radius: %.1f%n", c2.radius());

        // Money operations
        Money price1 = new Money(10.50, "USD");
        Money price2 = new Money(5.25, "USD");

        System.out.println("\nPrice 1: " + price1.formatted());
        System.out.println("Price 2: " + price2.formatted());

        Money total = price1.add(price2);
        System.out.println("Total: " + total.formatted());

        Money doubled = price1.multiply(2);
        System.out.println("Doubled: " + doubled.formatted());

        // Currency mismatch
        try {
            Money euros = new Money(10, "EUR");
            Money combined = price1.add(euros);
            System.out.println("Combined: " + combined);
        } catch (IllegalArgumentException e) {
            System.out.println("\nError: " + e.getMessage());
        }

        // Complex record with methods
        record Rectangle(double width, double height) {
            public double area() {
                return width * height;
            }

            public double perimeter() {
                return 2 * (width + height);
            }

            public boolean isSquare() {
                return width == height;
            }

            public Rectangle scale(double factor) {
                return new Rectangle(width * factor, height * factor);
            }
        }

        Rectangle rect = new Rectangle(10, 5);

        System.out.println("\nRectangle: " + rect);
        System.out.printf("Area: %.1f%n", rect.area());
        System.out.printf("Perimeter: %.1f%n", rect.perimeter());
        System.out.println("Is square: " + rect.isSquare());

        Rectangle scaled = rect.scale(2);
        System.out.println("Scaled 2x: " + scaled);

    }
}
// Custom methods in records
// Concept: custom methods - add behavior to records

record Circle(double radius) {
    // Add custom methods
    public double area() {
        return Math.PI * radius * radius;
    }

    public double circumference() {
        return 2 * Math.PI * radius;
    }

    public double diameter() {
        return 2 * radius;
    }

    // Static factory method
    public static Circle fromDiameter(double diameter) {
        return new Circle(diameter / 2);
    }
}

record Money(double amount, String currency) {
    // Methods with logic
    public Money add(Money other) {
        if (!currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
        return new Money(amount + other.amount, currency);
    }

    public Money multiply(double factor) {
        return new Money(amount * factor, currency);
    }

    public String formatted() {
        return String.format("%.2f %s", amount, currency);
    }
}

public class CustomMethods {
    public static void main(String[] args) {
        // Use custom methods
        Circle c1 = new Circle(10);

        System.out.println("Circle: " + c1);
        System.out.println("Radius: " + c1.radius());
        System.out.printf("Area: %.2f%n", c1.area());
        System.out.printf("Circumference: %.2f%n", c1.circumference());
        System.out.printf("Diameter: %.2f%n", c1.diameter());


        // Use static factory
        Circle c2 = Circle.fromDiameter(20);
        System.out.println("\nFrom diameter 20: " + c2);
        System.out.printf("Radius: %.1f%n", c2.radius());

        // Money operations
        Money price1 = new Money(10.50, "USD");
        Money price2 = new Money(5.25, "USD");

        System.out.println("\nPrice 1: " + price1.formatted());
        System.out.println("Price 2: " + price2.formatted());

        Money total = price1.add(price2);
        System.out.println("Total: " + total.formatted());

        Money doubled = price1.multiply(2);
        System.out.println("Doubled: " + doubled.formatted());

        // Currency mismatch
        try {
            Money euros = new Money(10, "EUR");
            Money combined = price1.add(euros);
            System.out.println("Combined: " + combined);
        } catch (IllegalArgumentException e) {
            System.out.println("\nError: " + e.getMessage());
        }

        // Complex record with methods
        record Rectangle(double width, double height) {
            public double area() {
                return width * height;
            }

            public double perimeter() {
                return 2 * (width + height);
            }

            public boolean isSquare() {
                return width == height;
            }

            public Rectangle scale(double factor) {
                return new Rectangle(width * factor, height * factor);
            }
        }

        Rectangle rect = new Rectangle(10, 5);

        System.out.println("\nRectangle: " + rect);
        System.out.printf("Area: %.1f%n", rect.area());
        System.out.printf("Perimeter: %.1f%n", rect.perimeter());
        System.out.println("Is square: " + rect.isSquare());

        Rectangle scaled = rect.scale(2);
        System.out.println("Scaled 2x: " + scaled);

    }
}
// Custom methods in records
// Concept: custom methods - add behavior to records

record Circle(double radius) {
    // Add custom methods
    public double area() {
        return Math.PI * radius * radius;
    }

    public double circumference() {
        return 2 * Math.PI * radius;
    }

    public double diameter() {
        return 2 * radius;
    }

    // Static factory method
    public static Circle fromDiameter(double diameter) {
        return new Circle(diameter / 2);
    }
}

record Money(double amount, String currency) {
    // Methods with logic
    public Money add(Money other) {
        if (!currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
        return new Money(amount + other.amount, currency);
    }

    public Money multiply(double factor) {
        return new Money(amount * factor, currency);
    }

    public String formatted() {
        return String.format("%.2f %s", amount, currency);
    }
}

public class CustomMethods {
    public static void main(String[] args) {
        // Use custom methods
        Circle c1 = new Circle(3.5);

        System.out.println("Circle: " + c1);
        System.out.println("Radius: " + c1.radius());
        System.out.printf("Area: %.2f%n", c1.area());
        System.out.printf("Circumference: %.2f%n", c1.circumference());
        System.out.printf("Diameter: %.2f%n", c1.diameter());


        // Use static factory
        Circle c2 = Circle.fromDiameter(20);
        System.out.println("\nFrom diameter 20: " + c2);
        System.out.printf("Radius: %.1f%n", c2.radius());

        // Money operations
        Money price1 = new Money(10.50, "USD");
        Money price2 = new Money(5.25, "USD");

        System.out.println("\nPrice 1: " + price1.formatted());
        System.out.println("Price 2: " + price2.formatted());

        Money total = price1.add(price2);
        System.out.println("Total: " + total.formatted());

        Money doubled = price1.multiply(2);
        System.out.println("Doubled: " + doubled.formatted());

        // Currency mismatch
        try {
            Money euros = new Money(10, "EUR");
            Money combined = price1.add(euros);
            System.out.println("Combined: " + combined);
        } catch (IllegalArgumentException e) {
            System.out.println("\nError: " + e.getMessage());
        }

        // Complex record with methods
        record Rectangle(double width, double height) {
            public double area() {
                return width * height;
            }

            public double perimeter() {
                return 2 * (width + height);
            }

            public boolean isSquare() {
                return width == height;
            }

            public Rectangle scale(double factor) {
                return new Rectangle(width * factor, height * factor);
            }
        }

        Rectangle rect = new Rectangle(10, 5);

        System.out.println("\nRectangle: " + rect);
        System.out.printf("Area: %.1f%n", rect.area());
        System.out.printf("Perimeter: %.1f%n", rect.perimeter());
        System.out.println("Is square: " + rect.isSquare());

        Rectangle scaled = rect.scale(2);
        System.out.println("Scaled 2x: " + scaled);

    }
}
  1. c1 ← Circle[radius=5.0]

    42public class CustomMethods {43    public static void main(String[] args) {44        // Use custom methods45        Circle c1→ Circle[radius=5.0] = new Circle(5.0);46        //@c1=new Circle(5.0), new Circle(10), new Circle(3.5)47        48        System.out.println("Circle: " + c1Circle[radius=5.0]);49        System.out.println("Radius: " + c1.radius());50        System.out.printf("Area: %.2f%n", c1.area());51        System.out.printf("Circumference: %.2f%n", c1.circumference());
    outputCircle: Circle[radius=5.0]
    Radius: 5.0
  2. public double area()

    5// Add custom methods6public double area() {7    return Math.PI * radius5.0 * radius;8}
  3. System.out.printf("Area: %.2f%n", c1.area());

    49System.out.println("Radius: " + c1.radius());50System.out.printf("Area: %.2f%n", c1.area());51System.out.printf("Circumference: %.2f%n", c1.circumference());52System.out.printf("Diameter: %.2f%n", c1.diameter());
  4. public double circumference()

    10public double circumference() {11    return 2 * Math.PI * radius5.0;12}
  5. System.out.printf("Circumference: %.2f%n", c1.circumference());

    50System.out.printf("Area: %.2f%n", c1.area());51System.out.printf("Circumference: %.2f%n", c1.circumference());52System.out.printf("Diameter: %.2f%n", c1.diameter());
  6. public double diameter()

    14public double diameter() {15    return 2 * radius5.0;16}
  7. System.out.printf("Diameter: %.2f%n", c1.diameter());

    51System.out.printf("Circumference: %.2f%n", c1.circumference());52System.out.printf("Diameter: %.2f%n", c1.diameter());5354//@help h155// Records can have instance and static methods56// Methods can access record components57// Records remain immutable - methods return new instances58//@end5960// Use static factory61Circle c2 = Circle.fromDiameter(20);62System.out.println("\nFrom diameter 20: " + c2);
  8. public static Circle fromDiameter(double diameter)

    18// Static factory method19public static Circle fromDiameter(double diameter20.0) {20    return new Circle(diameter / 2);21}
  9. c2 ← Circle[radius=10.0], price1 ← Money[amount=10.5, currency=USD]

    60// Use static factory61Circle c2→ Circle[radius=10.0] = Circle.fromDiameter(20);62System.out.println("\nFrom diameter 20: " + c2Circle[radius=10.0]);63System.out.printf("Radius: %.1f%n", c2.radius());6465// Money operations66Money price1→ Money[amount=10.5, currency=USD] = new Money(10.50, "USD");67Money price2→ Money[amount=5.25, currency=USD] = new Money(5.25, "USD");6869System.out.println("\nPrice 1: " + price1.formatted());70System.out.println("Price 2: " + price2.formatted());
    output
    From diameter 20: Circle[radius=10.0]
  10. public String formatted()

    pass 1 of 4
    37public String formatted() {38    return String.format("%.2f %s", amount10.5, currencyUSD);39}
    All 4 passes — pass 1 is the card above
    passamount
    110.5
    25.25
    315.75
    421.0
  11. System.out.println(" Price 1: " + price1.formatted());

    69System.out.println("\nPrice 1: " + price1.formatted());70System.out.println("Price 2: " + price2.formatted());
    output
    Price 1: 10.50 USD
  12. Money total = price1.add(price2);

    69System.out.println("\nPrice 1: " + price1.formatted());70System.out.println("Price 2: " + price2.formatted());7172Money total = price1.add(price2Money[amount=5.25, currency=USD]);73System.out.println("Total: " + total.formatted());
    outputPrice 2: 5.25 USD
  13. public Money add(Money other)

    pass 1 of 2
    25// Methods with logic26public Money add(Money otherMoney[amount=5.25, currency=USD]) {27    if (!currency.equals(other.currency)) {28        throw new IllegalArgumentException("Currency mismatch");29    }30    return new Money(amount + other.amount, currency);31}
  14. total ← Money[amount=15.75, currency=USD]

    72Money total→ Money[amount=15.75, currency=USD] = price1.add(price2Money[amount=5.25, currency=USD]);73System.out.println("Total: " + total.formatted());
  15. System.out.println("Total: " + total.formatted());

    72Money total = price1.add(price2);73System.out.println("Total: " + total.formatted());7475Money doubled = price1.multiply(2);76System.out.println("Doubled: " + doubled.formatted());
    outputTotal: 15.75 USD
  16. public Money multiply(double factor)

    33public Money multiply(double factor2.0) {34    return new Money(amount * factor, currency);35}
  17. doubled ← Money[amount=21.0, currency=USD]

    75Money doubled→ Money[amount=21.0, currency=USD] = price1.multiply(2);76System.out.println("Doubled: " + doubled.formatted());
  18. System.out.println("Doubled: " + doubled.formatted());

    75Money doubled = price1.multiply(2);76System.out.println("Doubled: " + doubled.formatted());
    outputDoubled: 21.00 USD
  19. euros ← Money[amount=10.0, currency=EUR]

    78// Currency mismatch79try {80    Money euros→ Money[amount=10.0, currency=EUR] = new Money(10, "EUR");81    Money combined = price1.add(eurosMoney[amount=10.0, currency=EUR]);82    System.out.println("Combined: " + combined);
  20. public Money add(Money other)

    pass 2 of 2
    25// Methods with logic26public Money add(Money otherMoney[amount=10.0, currency=EUR]) {27    if (!currency.equals(other.currency)) {
  21. if (!currency.equals(other.currency))

    26public Money add(Money other) {27    if (!currency.equals(other.currencyEUR)) {28        throw new IllegalArgumentException("Currency mismatch");29    }
  22. catch (IllegalArgumentException e)

    82    System.out.println("Combined: " + combined);83} catch (IllegalArgumentException ejava.lang.IllegalArgumentException: Currency mismatch) {84    System.out.println("\nError: " + e.getMessage());85}
    output
    Error: Currency mismatch
  23. rect ← Rectangle[width=10.0, height=5.0]

    106Rectangle rect→ Rectangle[width=10.0, height=5.0] = new Rectangle(10, 5);107108System.out.println("\nRectangle: " + rectRectangle[width=10.0, height=5.0]);109System.out.printf("Area: %.1f%n", rect.area());110System.out.printf("Perimeter: %.1f%n", rect.perimeter());
    output
    Rectangle: Rectangle[width=10.0, height=5.0]
  24. public double area()

    88record Rectangle(double width, double height) {89    public double area() {90        return width10.0 * height5.0;91    }
  25. System.out.printf("Area: %.1f%n", rect.area());

    108System.out.println("\nRectangle: " + rect);109System.out.printf("Area: %.1f%n", rect.area());110System.out.printf("Perimeter: %.1f%n", rect.perimeter());111System.out.println("Is square: " + rect.isSquare());
  26. public double perimeter()

    93public double perimeter() {94    return 2 * (width10.0 + height5.0);95}
  27. System.out.printf("Perimeter: %.1f%n", rect.perimeter());

    109System.out.printf("Area: %.1f%n", rect.area());110System.out.printf("Perimeter: %.1f%n", rect.perimeter());111System.out.println("Is square: " + rect.isSquare());
  28. public boolean isSquare()

    97public boolean isSquare() {98    return width10.0 == height5.0;99}
  29. System.out.println("Is square: " + rect.isSquare());

    110System.out.printf("Perimeter: %.1f%n", rect.perimeter());111System.out.println("Is square: " + rect.isSquare());112113Rectangle scaled = rect.scale(2);114System.out.println("Scaled 2x: " + scaled);
    outputIs square: false
  30. public Rectangle scale(double factor)

    101public Rectangle scale(double factor2.0) {102    return new Rectangle(width * factor, height * factor);103}
  31. scaled ← Rectangle[width=20.0, height=10.0]

    113Rectangle scaled→ Rectangle[width=20.0, height=10.0] = rect.scale(2);114System.out.println("Scaled 2x: " + scaledRectangle[width=20.0, height=10.0]);
    outputScaled 2x: Rectangle[width=20.0, height=10.0]
  1. c1 ← Circle[radius=10.0]

    42public class CustomMethods {43    public static void main(String[] args) {44        // Use custom methods45        Circle c1→ Circle[radius=10.0] = new Circle(10);46        47        System.out.println("Circle: " + c1Circle[radius=10.0]);48        System.out.println("Radius: " + c1.radius());49        System.out.printf("Area: %.2f%n", c1.area());50        System.out.printf("Circumference: %.2f%n", c1.circumference());
    outputCircle: Circle[radius=10.0]
    Radius: 10.0
  2. public double area()

    5// Add custom methods6public double area() {7    return Math.PI * radius10.0 * radius;8}
  3. System.out.printf("Area: %.2f%n", c1.area());

    48System.out.println("Radius: " + c1.radius());49System.out.printf("Area: %.2f%n", c1.area());50System.out.printf("Circumference: %.2f%n", c1.circumference());51System.out.printf("Diameter: %.2f%n", c1.diameter());
  4. public double circumference()

    10public double circumference() {11    return 2 * Math.PI * radius10.0;12}
  5. System.out.printf("Circumference: %.2f%n", c1.circumference());

    49System.out.printf("Area: %.2f%n", c1.area());50System.out.printf("Circumference: %.2f%n", c1.circumference());51System.out.printf("Diameter: %.2f%n", c1.diameter());
  6. public double diameter()

    14public double diameter() {15    return 2 * radius10.0;16}
  7. System.out.printf("Diameter: %.2f%n", c1.diameter());

    50System.out.printf("Circumference: %.2f%n", c1.circumference());51System.out.printf("Diameter: %.2f%n", c1.diameter());525354// Use static factory55Circle c2 = Circle.fromDiameter(20);56System.out.println("\nFrom diameter 20: " + c2);
  8. public static Circle fromDiameter(double diameter)

    18// Static factory method19public static Circle fromDiameter(double diameter20.0) {20    return new Circle(diameter / 2);21}
  9. c2 ← Circle[radius=10.0], price1 ← Money[amount=10.5, currency=USD]

    54// Use static factory55Circle c2→ Circle[radius=10.0] = Circle.fromDiameter(20);56System.out.println("\nFrom diameter 20: " + c2Circle[radius=10.0]);57System.out.printf("Radius: %.1f%n", c2.radius());5859// Money operations60Money price1→ Money[amount=10.5, currency=USD] = new Money(10.50, "USD");61Money price2→ Money[amount=5.25, currency=USD] = new Money(5.25, "USD");6263System.out.println("\nPrice 1: " + price1.formatted());64System.out.println("Price 2: " + price2.formatted());
    output
    From diameter 20: Circle[radius=10.0]
  10. public String formatted()

    pass 1 of 4
    37public String formatted() {38    return String.format("%.2f %s", amount10.5, currencyUSD);39}
    All 4 passes — pass 1 is the card above
    passamount
    110.5
    25.25
    315.75
    421.0
  11. System.out.println(" Price 1: " + price1.formatted());

    63System.out.println("\nPrice 1: " + price1.formatted());64System.out.println("Price 2: " + price2.formatted());
    output
    Price 1: 10.50 USD
  12. Money total = price1.add(price2);

    63System.out.println("\nPrice 1: " + price1.formatted());64System.out.println("Price 2: " + price2.formatted());6566Money total = price1.add(price2Money[amount=5.25, currency=USD]);67System.out.println("Total: " + total.formatted());
    outputPrice 2: 5.25 USD
  13. public Money add(Money other)

    pass 1 of 2
    25// Methods with logic26public Money add(Money otherMoney[amount=5.25, currency=USD]) {27    if (!currency.equals(other.currency)) {28        throw new IllegalArgumentException("Currency mismatch");29    }30    return new Money(amount + other.amount, currency);31}
  14. total ← Money[amount=15.75, currency=USD]

    66Money total→ Money[amount=15.75, currency=USD] = price1.add(price2Money[amount=5.25, currency=USD]);67System.out.println("Total: " + total.formatted());
  15. System.out.println("Total: " + total.formatted());

    66Money total = price1.add(price2);67System.out.println("Total: " + total.formatted());6869Money doubled = price1.multiply(2);70System.out.println("Doubled: " + doubled.formatted());
    outputTotal: 15.75 USD
  16. public Money multiply(double factor)

    33public Money multiply(double factor2.0) {34    return new Money(amount * factor, currency);35}
  17. doubled ← Money[amount=21.0, currency=USD]

    69Money doubled→ Money[amount=21.0, currency=USD] = price1.multiply(2);70System.out.println("Doubled: " + doubled.formatted());
  18. System.out.println("Doubled: " + doubled.formatted());

    69Money doubled = price1.multiply(2);70System.out.println("Doubled: " + doubled.formatted());
    outputDoubled: 21.00 USD
  19. euros ← Money[amount=10.0, currency=EUR]

    72// Currency mismatch73try {74    Money euros→ Money[amount=10.0, currency=EUR] = new Money(10, "EUR");75    Money combined = price1.add(eurosMoney[amount=10.0, currency=EUR]);76    System.out.println("Combined: " + combined);
  20. public Money add(Money other)

    pass 2 of 2
    25// Methods with logic26public Money add(Money otherMoney[amount=10.0, currency=EUR]) {27    if (!currency.equals(other.currency)) {
  21. if (!currency.equals(other.currency))

    26public Money add(Money other) {27    if (!currency.equals(other.currencyEUR)) {28        throw new IllegalArgumentException("Currency mismatch");29    }
  22. catch (IllegalArgumentException e)

    76    System.out.println("Combined: " + combined);77} catch (IllegalArgumentException ejava.lang.IllegalArgumentException: Currency mismatch) {78    System.out.println("\nError: " + e.getMessage());79}
    output
    Error: Currency mismatch
  23. rect ← Rectangle[width=10.0, height=5.0]

    100Rectangle rect→ Rectangle[width=10.0, height=5.0] = new Rectangle(10, 5);101102System.out.println("\nRectangle: " + rectRectangle[width=10.0, height=5.0]);103System.out.printf("Area: %.1f%n", rect.area());104System.out.printf("Perimeter: %.1f%n", rect.perimeter());
    output
    Rectangle: Rectangle[width=10.0, height=5.0]
  24. public double area()

    82record Rectangle(double width, double height) {83    public double area() {84        return width10.0 * height5.0;85    }
  25. System.out.printf("Area: %.1f%n", rect.area());

    102System.out.println("\nRectangle: " + rect);103System.out.printf("Area: %.1f%n", rect.area());104System.out.printf("Perimeter: %.1f%n", rect.perimeter());105System.out.println("Is square: " + rect.isSquare());
  26. public double perimeter()

    87public double perimeter() {88    return 2 * (width10.0 + height5.0);89}
  27. System.out.printf("Perimeter: %.1f%n", rect.perimeter());

    103System.out.printf("Area: %.1f%n", rect.area());104System.out.printf("Perimeter: %.1f%n", rect.perimeter());105System.out.println("Is square: " + rect.isSquare());
  28. public boolean isSquare()

    91public boolean isSquare() {92    return width10.0 == height5.0;93}
  29. System.out.println("Is square: " + rect.isSquare());

    104System.out.printf("Perimeter: %.1f%n", rect.perimeter());105System.out.println("Is square: " + rect.isSquare());106107Rectangle scaled = rect.scale(2);108System.out.println("Scaled 2x: " + scaled);
    outputIs square: false
  30. public Rectangle scale(double factor)

    95public Rectangle scale(double factor2.0) {96    return new Rectangle(width * factor, height * factor);97}
  31. scaled ← Rectangle[width=20.0, height=10.0]

    107Rectangle scaled→ Rectangle[width=20.0, height=10.0] = rect.scale(2);108System.out.println("Scaled 2x: " + scaledRectangle[width=20.0, height=10.0]);
    outputScaled 2x: Rectangle[width=20.0, height=10.0]
  1. c1 ← Circle[radius=3.5]

    42public class CustomMethods {43    public static void main(String[] args) {44        // Use custom methods45        Circle c1→ Circle[radius=3.5] = new Circle(3.5);46        47        System.out.println("Circle: " + c1Circle[radius=3.5]);48        System.out.println("Radius: " + c1.radius());49        System.out.printf("Area: %.2f%n", c1.area());50        System.out.printf("Circumference: %.2f%n", c1.circumference());
    outputCircle: Circle[radius=3.5]
    Radius: 3.5
  2. public double area()

    5// Add custom methods6public double area() {7    return Math.PI * radius3.5 * radius;8}
  3. System.out.printf("Area: %.2f%n", c1.area());

    48System.out.println("Radius: " + c1.radius());49System.out.printf("Area: %.2f%n", c1.area());50System.out.printf("Circumference: %.2f%n", c1.circumference());51System.out.printf("Diameter: %.2f%n", c1.diameter());
  4. public double circumference()

    10public double circumference() {11    return 2 * Math.PI * radius3.5;12}
  5. System.out.printf("Circumference: %.2f%n", c1.circumference());

    49System.out.printf("Area: %.2f%n", c1.area());50System.out.printf("Circumference: %.2f%n", c1.circumference());51System.out.printf("Diameter: %.2f%n", c1.diameter());
  6. public double diameter()

    14public double diameter() {15    return 2 * radius3.5;16}
  7. System.out.printf("Diameter: %.2f%n", c1.diameter());

    50System.out.printf("Circumference: %.2f%n", c1.circumference());51System.out.printf("Diameter: %.2f%n", c1.diameter());525354// Use static factory55Circle c2 = Circle.fromDiameter(20);56System.out.println("\nFrom diameter 20: " + c2);
  8. public static Circle fromDiameter(double diameter)

    18// Static factory method19public static Circle fromDiameter(double diameter20.0) {20    return new Circle(diameter / 2);21}
  9. c2 ← Circle[radius=10.0], price1 ← Money[amount=10.5, currency=USD]

    54// Use static factory55Circle c2→ Circle[radius=10.0] = Circle.fromDiameter(20);56System.out.println("\nFrom diameter 20: " + c2Circle[radius=10.0]);57System.out.printf("Radius: %.1f%n", c2.radius());5859// Money operations60Money price1→ Money[amount=10.5, currency=USD] = new Money(10.50, "USD");61Money price2→ Money[amount=5.25, currency=USD] = new Money(5.25, "USD");6263System.out.println("\nPrice 1: " + price1.formatted());64System.out.println("Price 2: " + price2.formatted());
    output
    From diameter 20: Circle[radius=10.0]
  10. public String formatted()

    pass 1 of 4
    37public String formatted() {38    return String.format("%.2f %s", amount10.5, currencyUSD);39}
    All 4 passes — pass 1 is the card above
    passamount
    110.5
    25.25
    315.75
    421.0
  11. System.out.println(" Price 1: " + price1.formatted());

    63System.out.println("\nPrice 1: " + price1.formatted());64System.out.println("Price 2: " + price2.formatted());
    output
    Price 1: 10.50 USD
  12. Money total = price1.add(price2);

    63System.out.println("\nPrice 1: " + price1.formatted());64System.out.println("Price 2: " + price2.formatted());6566Money total = price1.add(price2Money[amount=5.25, currency=USD]);67System.out.println("Total: " + total.formatted());
    outputPrice 2: 5.25 USD
  13. public Money add(Money other)

    pass 1 of 2
    25// Methods with logic26public Money add(Money otherMoney[amount=5.25, currency=USD]) {27    if (!currency.equals(other.currency)) {28        throw new IllegalArgumentException("Currency mismatch");29    }30    return new Money(amount + other.amount, currency);31}
  14. total ← Money[amount=15.75, currency=USD]

    66Money total→ Money[amount=15.75, currency=USD] = price1.add(price2Money[amount=5.25, currency=USD]);67System.out.println("Total: " + total.formatted());
  15. System.out.println("Total: " + total.formatted());

    66Money total = price1.add(price2);67System.out.println("Total: " + total.formatted());6869Money doubled = price1.multiply(2);70System.out.println("Doubled: " + doubled.formatted());
    outputTotal: 15.75 USD
  16. public Money multiply(double factor)

    33public Money multiply(double factor2.0) {34    return new Money(amount * factor, currency);35}
  17. doubled ← Money[amount=21.0, currency=USD]

    69Money doubled→ Money[amount=21.0, currency=USD] = price1.multiply(2);70System.out.println("Doubled: " + doubled.formatted());
  18. System.out.println("Doubled: " + doubled.formatted());

    69Money doubled = price1.multiply(2);70System.out.println("Doubled: " + doubled.formatted());
    outputDoubled: 21.00 USD
  19. euros ← Money[amount=10.0, currency=EUR]

    72// Currency mismatch73try {74    Money euros→ Money[amount=10.0, currency=EUR] = new Money(10, "EUR");75    Money combined = price1.add(eurosMoney[amount=10.0, currency=EUR]);76    System.out.println("Combined: " + combined);
  20. public Money add(Money other)

    pass 2 of 2
    25// Methods with logic26public Money add(Money otherMoney[amount=10.0, currency=EUR]) {27    if (!currency.equals(other.currency)) {
  21. if (!currency.equals(other.currency))

    26public Money add(Money other) {27    if (!currency.equals(other.currencyEUR)) {28        throw new IllegalArgumentException("Currency mismatch");29    }
  22. catch (IllegalArgumentException e)

    76    System.out.println("Combined: " + combined);77} catch (IllegalArgumentException ejava.lang.IllegalArgumentException: Currency mismatch) {78    System.out.println("\nError: " + e.getMessage());79}
    output
    Error: Currency mismatch
  23. rect ← Rectangle[width=10.0, height=5.0]

    100Rectangle rect→ Rectangle[width=10.0, height=5.0] = new Rectangle(10, 5);101102System.out.println("\nRectangle: " + rectRectangle[width=10.0, height=5.0]);103System.out.printf("Area: %.1f%n", rect.area());104System.out.printf("Perimeter: %.1f%n", rect.perimeter());
    output
    Rectangle: Rectangle[width=10.0, height=5.0]
  24. public double area()

    82record Rectangle(double width, double height) {83    public double area() {84        return width10.0 * height5.0;85    }
  25. System.out.printf("Area: %.1f%n", rect.area());

    102System.out.println("\nRectangle: " + rect);103System.out.printf("Area: %.1f%n", rect.area());104System.out.printf("Perimeter: %.1f%n", rect.perimeter());105System.out.println("Is square: " + rect.isSquare());
  26. public double perimeter()

    87public double perimeter() {88    return 2 * (width10.0 + height5.0);89}
  27. System.out.printf("Perimeter: %.1f%n", rect.perimeter());

    103System.out.printf("Area: %.1f%n", rect.area());104System.out.printf("Perimeter: %.1f%n", rect.perimeter());105System.out.println("Is square: " + rect.isSquare());
  28. public boolean isSquare()

    91public boolean isSquare() {92    return width10.0 == height5.0;93}
  29. System.out.println("Is square: " + rect.isSquare());

    104System.out.printf("Perimeter: %.1f%n", rect.perimeter());105System.out.println("Is square: " + rect.isSquare());106107Rectangle scaled = rect.scale(2);108System.out.println("Scaled 2x: " + scaled);
    outputIs square: false
  30. public Rectangle scale(double factor)

    95public Rectangle scale(double factor2.0) {96    return new Rectangle(width * factor, height * factor);97}
  31. scaled ← Rectangle[width=20.0, height=10.0]

    107Rectangle scaled→ Rectangle[width=20.0, height=10.0] = rect.scale(2);108System.out.println("Scaled 2x: " + scaledRectangle[width=20.0, height=10.0]);
    outputScaled 2x: Rectangle[width=20.0, height=10.0]

Records can have static and instance methods. Fields stay immutable.

Record vs class

When to use each.

RecordVsClass.java
Replay: real traced execution (multi-file project)
// Record vs class comparison
// Concept: record vs class - when to use each

// Traditional class
class MutablePoint {
    private int x;
    private int y;

    public MutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() { return x; }
    public int getY() { return y; }

    public void setX(int x) { this.x = x; }
    public void setY(int y) { this.y = y; }

    public void move(int dx, int dy) {
        x += dx;
        y += dy;
    }
}

// Record (immutable)
record ImmutablePoint(int x, int y) {
    public ImmutablePoint move(int dx, int dy) {
        return new ImmutablePoint(x + dx, y + dy);
    }
}

// Class with inheritance
class Shape {
    protected String color;

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

    public String getColor() {
        return color;
    }
}

class ColoredCircle extends Shape {
    private double radius;

    public ColoredCircle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }
}

// Record implementing interface
interface Describable {
    String describe();
}

record Product(String name, double price) implements Describable {
    @Override
    public String describe() {
        return name + " ($" + price + ")";
    }
}

public class RecordVsClass {
    public static void main(String[] args) {
        // Mutable class
        MutablePoint mp = new MutablePoint(10, 20);
        System.out.println("Mutable point: (" + mp.getX() + ", " + mp.getY() + ")");

        mp.setX(15);
        mp.move(5, 10);
        System.out.println("After changes: (" + mp.getX() + ", " + mp.getY() + ")");


        // Immutable record
        ImmutablePoint ip = new ImmutablePoint(10, 20);
        System.out.println("\nImmutable point: " + ip);

        ImmutablePoint ip2 = ip.move(5, 10);
        System.out.println("Original: " + ip);
        System.out.println("After move: " + ip2);

        // Inheritance example
        ColoredCircle circle = new ColoredCircle("red", 5.0);
        System.out.println("\nColored circle:");
        System.out.println("  Color: " + circle.getColor());
        System.out.println("  Radius: " + circle.getRadius());

        // Records cannot extend classes (always final)
        // record cannot be used here

        // Record with interface
        Product product = new Product("Laptop", 999.99);
        System.out.println("\nProduct: " + product.describe());

        // When to use records
        System.out.println("\nUse records for:");
        System.out.println("  ✓ DTOs (Data Transfer Objects)");
        System.out.println("  ✓ Immutable data models");
        System.out.println("  ✓ Return multiple values");
        System.out.println("  ✓ Map keys, set elements");

        System.out.println("\nUse classes for:");
        System.out.println("  ✓ Mutable state");
        System.out.println("  ✓ Inheritance hierarchies");
        System.out.println("  ✓ Private fields with logic");
        System.out.println("  ✓ Complex initialization");

        // Practical examples
        // Good use of record
        record ApiResponse(int status, String message, Object data) {}

        ApiResponse success = new ApiResponse(200, "OK", "Hello");
        ApiResponse error = new ApiResponse(404, "Not Found", null);

        System.out.println("\nAPI responses:");
        System.out.println("  " + success);
        System.out.println("  " + error);

        // Good use of class
        class Counter {
            private int count = 0;

            public void increment() { count++; }
            public int getCount() { return count; }
        }

        Counter counter = new Counter();
        counter.increment();
        counter.increment();
        System.out.println("\nCounter: " + counter.getCount());
    }
}
  1. public static void main(String[] args)

    71public class RecordVsClass {72    public static void main(String[] args) {73        // Mutable class74        MutablePoint mp = new MutablePoint(10, 20);75        System.out.println("Mutable point: (" + mp.getX() + ", " + mp.getY() + ")");
  2. this.x ← 10, this.y ← 20

    9public MutablePoint(int x10, int y20) {10    this.x→ 10 = x10;11    this.y→ 20 = y20;12}
  3. mp ← ⟨MutablePoint A⟩

    73// Mutable class74MutablePoint mp→ ⟨MutablePoint A⟩ = new MutablePoint(10, 20);75System.out.println("Mutable point: (" + mp.getX() + ", " + mp.getY() + ")");
  4. public int getX()

    pass 1 of 2
    14public int getX() { return x10; }15public int getY() { return y; }
  5. public int getY()

    pass 1 of 2
    14public int getX() { return x; }15public int getY() { return y20; }
  6. System.out.println("Mutable point: (" + mp.getX() + ", " + mp.getY() +…

    74MutablePoint mp = new MutablePoint(10, 20);75System.out.println("Mutable point: (" + mp.getX() + ", " + mp.getY() + ")");7677mp.setX(15);78mp.move(5, 10);
    outputMutable point: (10, 20)
  7. this.x ← 15

    17public void setX(int x15) { this.x→ 15 = x; }18public void setY(int y) { this.y = y; }
  8. mp.setX(15);

    77mp.setX(15);78mp.move(5, 10);79System.out.println("After changes: (" + mp.getX() + ", " + mp.getY() + ")");
  9. x ← 20, y ← 30

    20public void move(int dx5, int dy10) {21    x→ 20 += dx5;22    y→ 30 += dy10;23}
  10. mp.move(5, 10);

    77mp.setX(15);78mp.move(5, 10);79System.out.println("After changes: (" + mp.getX() + ", " + mp.getY() + ")");
  11. public int getX()

    pass 2 of 2
    14public int getX() { return x20; }15public int getY() { return y; }
  12. public int getY()

    pass 2 of 2
    14public int getX() { return x; }15public int getY() { return y30; }
  13. ip ← ImmutablePoint[x=10, y=20]

    78mp.move(5, 10);79System.out.println("After changes: (" + mp.getX() + ", " + mp.getY() + ")");8081//@help h182// Class: Mutable, can have inheritance, more flexible83// Record: Immutable, no inheritance, less boilerplate84// Use records for simple data carriers85// Use classes when you need mutability or inheritance86//@end8788// Immutable record89ImmutablePoint ip→ ImmutablePoint[x=10, y=20] = new ImmutablePoint(10, 20);90System.out.println("\nImmutable point: " + ipImmutablePoint[x=10, y=20]);9192ImmutablePoint ip2 = ip.move(5, 10);93System.out.println("Original: " + ip);
    outputAfter changes: (20, 30)
    
    Immutable point: ImmutablePoint[x=10, y=20]
  14. public ImmutablePoint move(int dx, int dy)

    27record ImmutablePoint(int x, int y) {28    public ImmutablePoint move(int dx5, int dy10) {29        return new ImmutablePoint(x + dx, y + dy);30    }
  15. ip2 ← ImmutablePoint[x=15, y=30]

    92ImmutablePoint ip2→ ImmutablePoint[x=15, y=30] = ip.move(5, 10);93System.out.println("Original: " + ipImmutablePoint[x=10, y=20]);94System.out.println("After move: " + ip2ImmutablePoint[x=15, y=30]);9596// Inheritance example97ColoredCircle circle = new ColoredCircle("red", 5.0);98System.out.println("\nColored circle:");
    outputOriginal: ImmutablePoint[x=10, y=20]
    After move: ImmutablePoint[x=15, y=30]
  16. this.color ← red

    37public Shape(String colorred) {38    this.color→ red = colorred;39}
  17. this.radius ← 5.0

    49public ColoredCircle(String colorred, double radius5.0) {50    super(color);51    this.radius→ 5.0 = radius5.0;52}
  18. circle ← ⟨ColoredCircle B⟩

    96// Inheritance example97ColoredCircle circle→ ⟨ColoredCircle B⟩ = new ColoredCircle("red", 5.0);98System.out.println("\nColored circle:");99System.out.println("  Color: " + circle.getColor());100System.out.println("  Radius: " + circle.getRadius());
    output
    Colored circle:
  19. public String getColor()

    41public String getColor() {42    return colorred;43}
  20. System.out.println(" Color: " + circle.getColor());

    98System.out.println("\nColored circle:");99System.out.println("  Color: " + circle.getColor());100System.out.println("  Radius: " + circle.getRadius());
    output  Color: red
  21. public double getRadius()

    54public double getRadius() {55    return radius5.0;56}
  22. product ← Product[name=Laptop, price=999.99]

    99System.out.println("  Color: " + circle.getColor());100System.out.println("  Radius: " + circle.getRadius());101102// Records cannot extend classes (always final)103// record cannot be used here104105// Record with interface106Product product→ Product[name=Laptop, price=999.99] = new Product("Laptop", 999.99);107System.out.println("\nProduct: " + product.describe());
    output  Radius: 5.0
  23. @Override public String describe()

    64record Product(String name, double price) implements Describable {65    @Override66    public String describe() {67        return nameLaptop + " ($" + price999.99 + ")";68    }
  24. error ← ApiResponse[status=404, message=Not Found, data=null]

    106Product product = new Product("Laptop", 999.99);107System.out.println("\nProduct: " + product.describe());108109// When to use records110System.out.println("\nUse records for:");111System.out.println("  ✓ DTOs (Data Transfer Objects)");112System.out.println("  ✓ Immutable data models");113System.out.println("  ✓ Return multiple values");114System.out.println("  ✓ Map keys, set elements");115116System.out.println("\nUse classes for:");117System.out.println("  ✓ Mutable state");118System.out.println("  ✓ Inheritance hierarchies");119System.out.println("  ✓ Private fields with logic");120System.out.println("  ✓ Complex initialization");121122// Practical examples123// Good use of record124record ApiResponse(int status, String message, Object data) {}125126ApiResponse success = new ApiResponse(200, "OK", "Hello");127ApiResponse error→ ApiResponse[status=404, message=Not Found, data=null] = new ApiResponse(404, "Not Found", null);128129System.out.println("\nAPI responses:");130System.out.println("  " + successApiResponse[status=200, message=OK, data=Hello]);131System.out.println("  " + errorApiResponse[status=404, message=Not Found, data=null]);132133// Good use of class134class Counter {135    private int count = 0;136    137    public void increment() { count++; }138    public int getCount() { return count; }139}140141Counter counter = new Counter();142counter.increment();143counter.increment();
    output
    Product: Laptop ($999.99)
    
    Use records for:
      ✓ DTOs (Data Transfer Objects)
      ✓ Immutable data models
      ✓ Return multiple values
      ✓ Map keys, set elements
    
    Use classes for:
      ✓ Mutable state
      ✓ Inheritance hierarchies
      ✓ Private fields with logic
      ✓ Complex initialization
    
    API responses:
      ApiResponse[status=200, message=OK, data=Hello]
      ApiResponse[status=404, message=Not Found, data=null]
  25. count ← 1

    pass 1 of 2
    137public void increment() { count→ 1++; }138public int getCount() { return count; }
  26. counter.increment();

    141Counter counter = new Counter();142counter.increment();143counter.increment();144System.out.println("\nCounter: " + counter.getCount());
  27. count ← 2

    pass 2 of 2
    137public void increment() { count→ 2++; }138public int getCount() { return count; }
  28. counter.increment();

    142    counter.increment();143    counter.increment();144    System.out.println("\nCounter: " + counter.getCount());145}
  29. public int getCount()

    137    public void increment() { count++; }138    public int getCount() { return count2; }139}
  30. System.out.println(" Counter: " + counter.getCount());

    143    counter.increment();144    System.out.println("\nCounter: " + counter.getCount());145}
    output
    Counter: 2

Record: pure data, immutable. Class: mutable state, complex behavior.

Exercise: Practical.java

Model API responses with records