You want List.of(1, 2, 3) to create a list. Static interface methods let interfaces have utility functions without needing a separate utility class. The method belongs to the interface itself, not to implementing classes.

Basic static method

Define a utility method on an interface.

StaticBasics.java
Replay: real traced execution (multi-file project)
// Basic Static Method Syntax

interface MathUtils {
    // Static method in interface
    static int square(int n) {
        return n * n;
    }

    static int cube(int n) {
        return n * n * n;
    }

    static boolean isEven(int n) {
        return n % 2 == 0;
    }

    static boolean isPositive(int n) {
        return n > 0;
    }

    // Static methods can call other static methods
    static boolean isPositiveEven(int n) {
        return isPositive(n) && isEven(n);
    }
}

// Class implementing interface
class Calculator implements MathUtils {
    // Does NOT inherit static methods!

    public int add(int a, int b) {
        return a + b;
    }
}

public class StaticBasics {
    public static void main(String[] args) {
        System.out.println("=== Static Interface Methods ===\n");

        // Call via interface name
        System.out.println("MathUtils.square(5) = " + MathUtils.square(5));
        System.out.println("MathUtils.cube(3) = " + MathUtils.cube(3));
        System.out.println("MathUtils.isEven(4) = " + MathUtils.isEven(4));
        System.out.println("MathUtils.isEven(7) = " + MathUtils.isEven(7));
        System.out.println("MathUtils.isPositiveEven(6) = " + MathUtils.isPositiveEven(6));

        System.out.println("\n=== Using with Calculator ===");
        Calculator calc = new Calculator();
        System.out.println("calc.add(2, 3) = " + calc.add(2, 3));

        // Cannot call static via instance!
        // calc.square(5);  // COMPILE ERROR!
        // Must use interface name
        System.out.println("MathUtils.square(5) = " + MathUtils.square(5));

        System.out.println("\n=== Static vs Instance ===");
        System.out.println("""
            Static methods in interfaces:
            - Belong to the interface itself
            - Called via InterfaceName.method()
            - NOT inherited by implementing classes
            - Cannot be overridden
            - Good for utility functions

            Default methods (for comparison):
            - Belong to instances
            - Called via object.method()
            - ARE inherited by implementing classes
            - CAN be overridden
            """);
    }
}
  1. public static void main(String[] args)

    36public class StaticBasics {37    public static void main(String[] args) {38        System.out.println("=== Static Interface Methods ===\n");39        40        // Call via interface name //?callviainterface41        System.out.println("MathUtils.square(5) = " + MathUtils.square(5));42        System.out.println("MathUtils.cube(3) = " + MathUtils.cube(3));
    output=== Static Interface Methods ===
  2. static int square(int n)

    pass 1 of 2
    4// Static method in interface //?staticmethod5static int square(int n5) { //?statickeyword6    return n5 * n;7}
  3. System.out.println("MathUtils.square(5) = " + MathUtils.square(5));

    40// Call via interface name //?callviainterface41System.out.println("MathUtils.square(5) = " + MathUtils.square(5));42System.out.println("MathUtils.cube(3) = " + MathUtils.cube(3));43System.out.println("MathUtils.isEven(4) = " + MathUtils.isEven(4));
    outputMathUtils.square(5) = 25
  4. static int cube(int n)

    9static int cube(int n3) {10    return n3 * n * n;11}
  5. System.out.println("MathUtils.cube(3) = " + MathUtils.cube(3));

    41System.out.println("MathUtils.square(5) = " + MathUtils.square(5));42System.out.println("MathUtils.cube(3) = " + MathUtils.cube(3));43System.out.println("MathUtils.isEven(4) = " + MathUtils.isEven(4));44System.out.println("MathUtils.isEven(7) = " + MathUtils.isEven(7));
    outputMathUtils.cube(3) = 27
  6. static boolean isEven(int n)

    pass 1 of 3
    13static boolean isEven(int n4) { //?iseven14    return n4 % 2 == 0;15}
    All 3 passes — pass 1 is the card above
    passn
    14
    27
    36
  7. System.out.println("MathUtils.isEven(4) = " + MathUtils.isEven(4));

    42System.out.println("MathUtils.cube(3) = " + MathUtils.cube(3));43System.out.println("MathUtils.isEven(4) = " + MathUtils.isEven(4));44System.out.println("MathUtils.isEven(7) = " + MathUtils.isEven(7));45System.out.println("MathUtils.isPositiveEven(6) = " + MathUtils.isPositiveEven(6));
    outputMathUtils.isEven(4) = true
  8. System.out.println("MathUtils.isEven(7) = " + MathUtils.isEven(7));

    43System.out.println("MathUtils.isEven(4) = " + MathUtils.isEven(4));44System.out.println("MathUtils.isEven(7) = " + MathUtils.isEven(7));45System.out.println("MathUtils.isPositiveEven(6) = " + MathUtils.isPositiveEven(6));
    outputMathUtils.isEven(7) = false
  9. static boolean isPositiveEven(int n)

    21// Static methods can call other static methods //?callstatic22static boolean isPositiveEven(int n6) {23    return isPositive(n6) && isEven(n);24}
  10. static boolean isPositive(int n)

    17static boolean isPositive(int n6) {18    return n6 > 0;19}
  11. calc ← ⟨Calculator A⟩

    44System.out.println("MathUtils.isEven(7) = " + MathUtils.isEven(7));45System.out.println("MathUtils.isPositiveEven(6) = " + MathUtils.isPositiveEven(6));4647System.out.println("\n=== Using with Calculator ===");48Calculator calc→ ⟨Calculator A⟩ = new Calculator();49System.out.println("calc.add(2, 3) = " + calc.add(2, 3));
    outputMathUtils.isPositiveEven(6) = true
    
    === Using with Calculator ===
  12. public int add(int a, int b)

    31public int add(int a2, int b3) {32    return a2 + b3;33}
  13. System.out.println("calc.add(2, 3) = " + calc.add(2, 3));

    48Calculator calc = new Calculator();49System.out.println("calc.add(2, 3) = " + calc.add(2, 3));5051// Cannot call static via instance! //?cannotinstance52// calc.square(5);  // COMPILE ERROR!53// Must use interface name54System.out.println("MathUtils.square(5) = " + MathUtils.square(5));
    outputcalc.add(2, 3) = 5
  14. static int square(int n)

    pass 2 of 2
    4// Static method in interface //?staticmethod5static int square(int n5) { //?statickeyword6    return n5 * n;7}
  15. System.out.println("MathUtils.square(5) = " + MathUtils.square(5));

    53    // Must use interface name54    System.out.println("MathUtils.square(5) = " + MathUtils.square(5));55    56    System.out.println("\n=== Static vs Instance ===");57    System.out.println("""58        Static methods in interfaces:59        - Belong to the interface itself60        - Called via InterfaceName.method()61        - NOT inherited by implementing classes62        - Cannot be overridden63        - Good for utility functions64        65        Default methods (for comparison):66        - Belong to instances67        - Called via object.method()68        - ARE inherited by implementing classes69        - CAN be overridden70        """);71}
    outputMathUtils.square(5) = 25
    
    === Static vs Instance ===
    Static methods in interfaces:
    - Belong to the interface itself
    - Called via InterfaceName.method()
    - NOT inherited by implementing classes
    - Cannot be overridden
    - Good for utility functions
    
    Default methods (for comparison):
    - Belong to instances
    - Called via object.method()
    - ARE inherited by implementing classes
    - CAN be overridden

static methods belong to interface. Call via InterfaceName.method().

static interface method Method that belongs to interface, not implementations. Not inherited.

Factory methods

Static methods that create instances.

circleRadius
FactoryMethods.java
Replay: real traced execution (multi-file project)
// Factory Methods in Interfaces

interface Shape {
    String getName();
    double getArea();

    // Static factory methods
    static Shape circle(double radius) {
        return new Circle(radius);
    }

    static Shape rectangle(double width, double height) {
        return new Rectangle(width, height);
    }

    static Shape square(double side) {
        return new Rectangle(side, side);  // Square is special rectangle
    }
}

// Implementation classes can be package-private
class Circle implements Shape {
    private double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public String getName() {
        return "Circle(r=" + radius + ")";
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

class Rectangle implements Shape {
    private double width, height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public String getName() {
        return "Rectangle(" + width + "x" + height + ")";
    }

    @Override
    public double getArea() {
        return width * height;
    }
}

// Another example: immutable collections pattern
interface ImmutableList<T> {
    int size();
    T get(int index);

    // Factory methods like List.of()
    static <T> ImmutableList<T> of() {
        return new EmptyList<>();
    }

    static <T> ImmutableList<T> of(T item) {
        return new SingleList<>(item);
    }

    @SafeVarargs
    static <T> ImmutableList<T> of(T... items) {
        return new ArrayBackedList<>(items.clone());
    }
}

class EmptyList<T> implements ImmutableList<T> {
    @Override public int size() { return 0; }
    @Override public T get(int index) { throw new IndexOutOfBoundsException(); }
}

class SingleList<T> implements ImmutableList<T> {
    private T item;
    SingleList(T item) { this.item = item; }
    @Override public int size() { return 1; }
    @Override public T get(int index) {
        if (index != 0) throw new IndexOutOfBoundsException();
        return item;
    }
}

class ArrayBackedList<T> implements ImmutableList<T> {
    private Object[] items;
    ArrayBackedList(Object[] items) { this.items = items; }
    @Override public int size() { return items.length; }
    @Override @SuppressWarnings("unchecked")
    public T get(int index) { return (T) items[index]; }
}

public class FactoryMethods {
    public static void main(String[] args) {
        System.out.println("=== Shape Factory Methods ===\n");

        // Create shapes via interface
        double circleRadius = 5.0;
        Shape circle = Shape.circle(circleRadius);
        Shape rect = Shape.rectangle(4, 6);
        Shape square = Shape.square(3);

        System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));
        System.out.println(rect.getName() + " area: " + rect.getArea());
        System.out.println(square.getName() + " area: " + square.getArea());

        System.out.println("\n=== Benefits of Factory Methods ===");
        System.out.println("""
            1. Hide implementations - users don't know about Circle/Rectangle
            2. Return appropriate subtype - square() returns Rectangle
            3. Caching possible - can reuse instances
            4. Validation - can check parameters
            """);

        System.out.println("=== Immutable List Factory ===\n");

        // Like Java's List.of()
        ImmutableList<String> empty = ImmutableList.of();
        ImmutableList<String> single = ImmutableList.of("hello");
        ImmutableList<String> multi = ImmutableList.of("a", "b", "c");

        System.out.println("empty.size() = " + empty.size());
        System.out.println("single.get(0) = " + single.get(0));
        System.out.println("multi.size() = " + multi.size());

        for (int i = 0; i < multi.size(); i++) {
            System.out.println("multi.get(" + i + ") = " + multi.get(i));
        }

        System.out.println("\n=== Real Java Examples ===");
        System.out.println("""
            Java's static factory methods:
            - List.of("a", "b", "c")
            - Set.of(1, 2, 3)
            - Map.of("key", "value")
            - Optional.of(value)
            - Optional.empty()
            - Stream.of(items)
            - Comparator.comparing(keyExtractor)
            """);
    }
}
// Factory Methods in Interfaces

interface Shape {
    String getName();
    double getArea();

    // Static factory methods
    static Shape circle(double radius) {
        return new Circle(radius);
    }

    static Shape rectangle(double width, double height) {
        return new Rectangle(width, height);
    }

    static Shape square(double side) {
        return new Rectangle(side, side);  // Square is special rectangle
    }
}

// Implementation classes can be package-private
class Circle implements Shape {
    private double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public String getName() {
        return "Circle(r=" + radius + ")";
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

class Rectangle implements Shape {
    private double width, height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public String getName() {
        return "Rectangle(" + width + "x" + height + ")";
    }

    @Override
    public double getArea() {
        return width * height;
    }
}

// Another example: immutable collections pattern
interface ImmutableList<T> {
    int size();
    T get(int index);

    // Factory methods like List.of()
    static <T> ImmutableList<T> of() {
        return new EmptyList<>();
    }

    static <T> ImmutableList<T> of(T item) {
        return new SingleList<>(item);
    }

    @SafeVarargs
    static <T> ImmutableList<T> of(T... items) {
        return new ArrayBackedList<>(items.clone());
    }
}

class EmptyList<T> implements ImmutableList<T> {
    @Override public int size() { return 0; }
    @Override public T get(int index) { throw new IndexOutOfBoundsException(); }
}

class SingleList<T> implements ImmutableList<T> {
    private T item;
    SingleList(T item) { this.item = item; }
    @Override public int size() { return 1; }
    @Override public T get(int index) {
        if (index != 0) throw new IndexOutOfBoundsException();
        return item;
    }
}

class ArrayBackedList<T> implements ImmutableList<T> {
    private Object[] items;
    ArrayBackedList(Object[] items) { this.items = items; }
    @Override public int size() { return items.length; }
    @Override @SuppressWarnings("unchecked")
    public T get(int index) { return (T) items[index]; }
}

public class FactoryMethods {
    public static void main(String[] args) {
        System.out.println("=== Shape Factory Methods ===\n");

        // Create shapes via interface
        double circleRadius = 2.5;
        Shape circle = Shape.circle(circleRadius);
        Shape rect = Shape.rectangle(4, 6);
        Shape square = Shape.square(3);

        System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));
        System.out.println(rect.getName() + " area: " + rect.getArea());
        System.out.println(square.getName() + " area: " + square.getArea());

        System.out.println("\n=== Benefits of Factory Methods ===");
        System.out.println("""
            1. Hide implementations - users don't know about Circle/Rectangle
            2. Return appropriate subtype - square() returns Rectangle
            3. Caching possible - can reuse instances
            4. Validation - can check parameters
            """);

        System.out.println("=== Immutable List Factory ===\n");

        // Like Java's List.of()
        ImmutableList<String> empty = ImmutableList.of();
        ImmutableList<String> single = ImmutableList.of("hello");
        ImmutableList<String> multi = ImmutableList.of("a", "b", "c");

        System.out.println("empty.size() = " + empty.size());
        System.out.println("single.get(0) = " + single.get(0));
        System.out.println("multi.size() = " + multi.size());

        for (int i = 0; i < multi.size(); i++) {
            System.out.println("multi.get(" + i + ") = " + multi.get(i));
        }

        System.out.println("\n=== Real Java Examples ===");
        System.out.println("""
            Java's static factory methods:
            - List.of("a", "b", "c")
            - Set.of(1, 2, 3)
            - Map.of("key", "value")
            - Optional.of(value)
            - Optional.empty()
            - Stream.of(items)
            - Comparator.comparing(keyExtractor)
            """);
    }
}
// Factory Methods in Interfaces

interface Shape {
    String getName();
    double getArea();

    // Static factory methods
    static Shape circle(double radius) {
        return new Circle(radius);
    }

    static Shape rectangle(double width, double height) {
        return new Rectangle(width, height);
    }

    static Shape square(double side) {
        return new Rectangle(side, side);  // Square is special rectangle
    }
}

// Implementation classes can be package-private
class Circle implements Shape {
    private double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public String getName() {
        return "Circle(r=" + radius + ")";
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

class Rectangle implements Shape {
    private double width, height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public String getName() {
        return "Rectangle(" + width + "x" + height + ")";
    }

    @Override
    public double getArea() {
        return width * height;
    }
}

// Another example: immutable collections pattern
interface ImmutableList<T> {
    int size();
    T get(int index);

    // Factory methods like List.of()
    static <T> ImmutableList<T> of() {
        return new EmptyList<>();
    }

    static <T> ImmutableList<T> of(T item) {
        return new SingleList<>(item);
    }

    @SafeVarargs
    static <T> ImmutableList<T> of(T... items) {
        return new ArrayBackedList<>(items.clone());
    }
}

class EmptyList<T> implements ImmutableList<T> {
    @Override public int size() { return 0; }
    @Override public T get(int index) { throw new IndexOutOfBoundsException(); }
}

class SingleList<T> implements ImmutableList<T> {
    private T item;
    SingleList(T item) { this.item = item; }
    @Override public int size() { return 1; }
    @Override public T get(int index) {
        if (index != 0) throw new IndexOutOfBoundsException();
        return item;
    }
}

class ArrayBackedList<T> implements ImmutableList<T> {
    private Object[] items;
    ArrayBackedList(Object[] items) { this.items = items; }
    @Override public int size() { return items.length; }
    @Override @SuppressWarnings("unchecked")
    public T get(int index) { return (T) items[index]; }
}

public class FactoryMethods {
    public static void main(String[] args) {
        System.out.println("=== Shape Factory Methods ===\n");

        // Create shapes via interface
        double circleRadius = 8.0;
        Shape circle = Shape.circle(circleRadius);
        Shape rect = Shape.rectangle(4, 6);
        Shape square = Shape.square(3);

        System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));
        System.out.println(rect.getName() + " area: " + rect.getArea());
        System.out.println(square.getName() + " area: " + square.getArea());

        System.out.println("\n=== Benefits of Factory Methods ===");
        System.out.println("""
            1. Hide implementations - users don't know about Circle/Rectangle
            2. Return appropriate subtype - square() returns Rectangle
            3. Caching possible - can reuse instances
            4. Validation - can check parameters
            """);

        System.out.println("=== Immutable List Factory ===\n");

        // Like Java's List.of()
        ImmutableList<String> empty = ImmutableList.of();
        ImmutableList<String> single = ImmutableList.of("hello");
        ImmutableList<String> multi = ImmutableList.of("a", "b", "c");

        System.out.println("empty.size() = " + empty.size());
        System.out.println("single.get(0) = " + single.get(0));
        System.out.println("multi.size() = " + multi.size());

        for (int i = 0; i < multi.size(); i++) {
            System.out.println("multi.get(" + i + ") = " + multi.get(i));
        }

        System.out.println("\n=== Real Java Examples ===");
        System.out.println("""
            Java's static factory methods:
            - List.of("a", "b", "c")
            - Set.of(1, 2, 3)
            - Map.of("key", "value")
            - Optional.of(value)
            - Optional.empty()
            - Stream.of(items)
            - Comparator.comparing(keyExtractor)
            """);
    }
}
  1. circleRadius ← 5.0

    102public class FactoryMethods {103    public static void main(String[] args) {104        System.out.println("=== Shape Factory Methods ===\n");105        106        // Create shapes via interface //?usefactory107        double circleRadius→ 5.0 = 5.0;  //@circleRadius=5.0, 2.5, 8.0108        Shape circle = Shape.circle(circleRadius5.0);109        Shape rect = Shape.rectangle(4, 6);
    output=== Shape Factory Methods ===
  2. static Shape circle(double radius)

    7// Static factory methods //?factory8static Shape circle(double radius5.0) { //?circlefactory9    return new Circle(radius);10}
  3. this.radius ← 5.0

    25Circle(double radius5.0) { //?packageconstructor26    this.radius→ 5.0 = radius5.0;27}
  4. circle ← ⟨Circle A⟩

    107double circleRadius = 5.0;  //@circleRadius=5.0, 2.5, 8.0108Shape circle→ ⟨Circle A⟩ = Shape.circle(circleRadius5.0);109Shape rect = Shape.rectangle(4, 6);110Shape square = Shape.square(3);
  5. static Shape rectangle(double width, double height)

    12static Shape rectangle(double width4.0, double height6.0) { //?rectanglefactory13    return new Rectangle(width, height);14}
  6. this.width ← 4.0, this.height ← 6.0

    pass 1 of 2
    43Rectangle(double width4.0, double height6.0) {44    this.width→ 4.0 = width4.0;45    this.height→ 6.0 = height6.0;46}
  7. rect ← ⟨Rectangle B⟩

    108Shape circle = Shape.circle(circleRadius);109Shape rect→ ⟨Rectangle B⟩ = Shape.rectangle(4, 6);110Shape square = Shape.square(3);
  8. static Shape square(double side)

    16static Shape square(double side3.0) { //?squarefactory17    return new Rectangle(side, side);  // Square is special rectangle18}
  9. this.width ← 3.0, this.height ← 3.0

    pass 2 of 2
    43Rectangle(double width3.0, double height3.0) {44    this.width→ 3.0 = width3.0;45    this.height→ 3.0 = height3.0;46}
  10. square ← ⟨Rectangle C⟩

    109Shape rect = Shape.rectangle(4, 6);110Shape square→ ⟨Rectangle C⟩ = Shape.square(3);111112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());
  11. @Override public String getName()

    29@Override30public String getName() {31    return "Circle(r=" + radius5.0 + ")";32}
  12. @Override public double getArea()

    34@Override35public double getArea() {36    return Math.PI * radius5.0 * radius;37}
  13. System.out.println(circle.getName() + " area: " + String.format("%.2f"…

    112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());
    outputCircle(r=5.0) area: 78.54
  14. @Override public String getName()

    pass 1 of 2
    48@Override49public String getName() {50    return "Rectangle(" + width4.0 + "x" + height6.0 + ")";51}
  15. @Override public double getArea()

    pass 1 of 2
    53@Override54public double getArea() {55    return width4.0 * height6.0;56}
  16. System.out.println(rect.getName() + " area: " + rect.getArea());

    112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());
    outputRectangle(4.0x6.0) area: 24.0
  17. @Override public String getName()

    pass 2 of 2
    48@Override49public String getName() {50    return "Rectangle(" + width3.0 + "x" + height3.0 + ")";51}
  18. @Override public double getArea()

    pass 2 of 2
    53@Override54public double getArea() {55    return width3.0 * height3.0;56}
  19. System.out.println(square.getName() + " area: " + square.getArea());

    113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());115116System.out.println("\n=== Benefits of Factory Methods ===");117System.out.println("""118    1. Hide implementations - users don't know about Circle/Rectangle119    2. Return appropriate subtype - square() returns Rectangle120    3. Caching possible - can reuse instances121    4. Validation - can check parameters122    """);123124System.out.println("=== Immutable List Factory ===\n");125126// Like Java's List.of() //?likelistof127ImmutableList<String> empty = ImmutableList.of();128ImmutableList<String> single = ImmutableList.of("hello");
    outputRectangle(3.0x3.0) area: 9.0
    
    === Benefits of Factory Methods ===
    1. Hide implementations - users don't know about Circle/Rectangle
    2. Return appropriate subtype - square() returns Rectangle
    3. Caching possible - can reuse instances
    4. Validation - can check parameters
    === Immutable List Factory ===
  20. empty ← ⟨EmptyList D⟩

    126// Like Java's List.of() //?likelistof127ImmutableList<String> empty→ ⟨EmptyList D⟩ = ImmutableList.of();128ImmutableList<String> single = ImmutableList.of("hello");129ImmutableList<String> multi = ImmutableList.of("a", "b", "c");
  21. static <T> ImmutableList<T> of(T item)

    69static <T> ImmutableList<T> of(T itemhello) { //?singlelist70    return new SingleList<>(item);71}
  22. this.item ← hello

    85private T item;86SingleList(T itemhello) { this.item→ hello = item; }87@Override public int size() { return 1; }
  23. single ← ⟨SingleList E⟩

    127ImmutableList<String> empty = ImmutableList.of();128ImmutableList<String> single→ ⟨SingleList E⟩ = ImmutableList.of("hello");129ImmutableList<String> multi = ImmutableList.of("a", "b", "c");
  24. @SafeVarargs static <T> ImmutableList<T> of(T... items)

    73@SafeVarargs74static <T> ImmutableList<T> of(T... items) { //?vararglist75    return new ArrayBackedList<>(items.clone());76}
  25. ArrayBackedList(Object[] items)

    95private Object[] items;96ArrayBackedList(Object[] items) { this.items = items; }97@Override public int size() { return items.length; }
  26. multi ← ⟨ArrayBackedList F⟩

    128ImmutableList<String> single = ImmutableList.of("hello");129ImmutableList<String> multi→ ⟨ArrayBackedList F⟩ = ImmutableList.of("a", "b", "c");130131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));
  27. System.out.println("empty.size() = " + empty.size());

    131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputempty.size() = 0
  28. @Override public T get(int index)

    87@Override public int size() { return 1; }88@Override public T get(int index0) {89    if (index != 0) throw new IndexOutOfBoundsException();90    return itemhello;91}
  29. System.out.println("single.get(0) = " + single.get(0));

    131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputsingle.get(0) = hello
  30. @Override public int size()

    pass 1 of 5
    96ArrayBackedList(Object[] items) { this.items = items; }97@Override public int size() { return items.length3; }98@Override @SuppressWarnings("unchecked")
  31. System.out.println("multi.size() = " + multi.size());

    132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputmulti.size() = 3
  32. for (int i = 0; i < multi.size(); i++)

    pass 1 of 3
    135for (int i0 = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i0 + ") = " + multi.get(i));137}
    All 3 passes — pass 1 is the card above
    passi
    10
    21
    32
  33. @Override @SuppressWarnings("unchecked") public T get(int index)

    pass 1 of 3
    97    @Override public int size() { return items.length; }98    @Override @SuppressWarnings("unchecked")99    public T get(int index0) { return (T) items[index]a; }100}
    All 3 passes — pass 1 is the card above
    passindexitems[index]
    10a
    21b
    32c
  34. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i0 + ") = " + multi.get(i));137}
    outputmulti.get(0) = a
  35. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i1 + ") = " + multi.get(i));137}
    outputmulti.get(1) = b
  36. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i2 + ") = " + multi.get(i));137}
    outputmulti.get(2) = c
  37. System.out.println(" === Real Java Examples ===");

    139    System.out.println("\n=== Real Java Examples ===");140    System.out.println("""141        Java's static factory methods:142        - List.of("a", "b", "c")143        - Set.of(1, 2, 3)144        - Map.of("key", "value")145        - Optional.of(value)146        - Optional.empty()147        - Stream.of(items)148        - Comparator.comparing(keyExtractor)149        """);150}
    output
    === Real Java Examples ===
    Java's static factory methods:
    - List.of("a", "b", "c")
    - Set.of(1, 2, 3)
    - Map.of("key", "value")
    - Optional.of(value)
    - Optional.empty()
    - Stream.of(items)
    - Comparator.comparing(keyExtractor)
  1. circleRadius ← 2.5

    102public class FactoryMethods {103    public static void main(String[] args) {104        System.out.println("=== Shape Factory Methods ===\n");105        106        // Create shapes via interface107        double circleRadius→ 2.5 = 2.5;108        Shape circle = Shape.circle(circleRadius2.5);109        Shape rect = Shape.rectangle(4, 6);
    output=== Shape Factory Methods ===
  2. static Shape circle(double radius)

    7// Static factory methods8static Shape circle(double radius2.5) {9    return new Circle(radius);10}
  3. this.radius ← 2.5

    25Circle(double radius2.5) {26    this.radius→ 2.5 = radius2.5;27}
  4. circle ← ⟨Circle A⟩

    107double circleRadius = 2.5;108Shape circle→ ⟨Circle A⟩ = Shape.circle(circleRadius2.5);109Shape rect = Shape.rectangle(4, 6);110Shape square = Shape.square(3);
  5. static Shape rectangle(double width, double height)

    12static Shape rectangle(double width4.0, double height6.0) {13    return new Rectangle(width, height);14}
  6. this.width ← 4.0, this.height ← 6.0

    pass 1 of 2
    43Rectangle(double width4.0, double height6.0) {44    this.width→ 4.0 = width4.0;45    this.height→ 6.0 = height6.0;46}
  7. rect ← ⟨Rectangle B⟩

    108Shape circle = Shape.circle(circleRadius);109Shape rect→ ⟨Rectangle B⟩ = Shape.rectangle(4, 6);110Shape square = Shape.square(3);
  8. static Shape square(double side)

    16static Shape square(double side3.0) {17    return new Rectangle(side, side);  // Square is special rectangle18}
  9. this.width ← 3.0, this.height ← 3.0

    pass 2 of 2
    43Rectangle(double width3.0, double height3.0) {44    this.width→ 3.0 = width3.0;45    this.height→ 3.0 = height3.0;46}
  10. square ← ⟨Rectangle C⟩

    109Shape rect = Shape.rectangle(4, 6);110Shape square→ ⟨Rectangle C⟩ = Shape.square(3);111112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());
  11. @Override public String getName()

    29@Override30public String getName() {31    return "Circle(r=" + radius2.5 + ")";32}
  12. @Override public double getArea()

    34@Override35public double getArea() {36    return Math.PI * radius2.5 * radius;37}
  13. System.out.println(circle.getName() + " area: " + String.format("%.2f"…

    112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());
    outputCircle(r=2.5) area: 19.63
  14. @Override public String getName()

    pass 1 of 2
    48@Override49public String getName() {50    return "Rectangle(" + width4.0 + "x" + height6.0 + ")";51}
  15. @Override public double getArea()

    pass 1 of 2
    53@Override54public double getArea() {55    return width4.0 * height6.0;56}
  16. System.out.println(rect.getName() + " area: " + rect.getArea());

    112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());
    outputRectangle(4.0x6.0) area: 24.0
  17. @Override public String getName()

    pass 2 of 2
    48@Override49public String getName() {50    return "Rectangle(" + width3.0 + "x" + height3.0 + ")";51}
  18. @Override public double getArea()

    pass 2 of 2
    53@Override54public double getArea() {55    return width3.0 * height3.0;56}
  19. System.out.println(square.getName() + " area: " + square.getArea());

    113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());115116System.out.println("\n=== Benefits of Factory Methods ===");117System.out.println("""118    1. Hide implementations - users don't know about Circle/Rectangle119    2. Return appropriate subtype - square() returns Rectangle120    3. Caching possible - can reuse instances121    4. Validation - can check parameters122    """);123124System.out.println("=== Immutable List Factory ===\n");125126// Like Java's List.of()127ImmutableList<String> empty = ImmutableList.of();128ImmutableList<String> single = ImmutableList.of("hello");
    outputRectangle(3.0x3.0) area: 9.0
    
    === Benefits of Factory Methods ===
    1. Hide implementations - users don't know about Circle/Rectangle
    2. Return appropriate subtype - square() returns Rectangle
    3. Caching possible - can reuse instances
    4. Validation - can check parameters
    === Immutable List Factory ===
  20. empty ← ⟨EmptyList D⟩

    126// Like Java's List.of()127ImmutableList<String> empty→ ⟨EmptyList D⟩ = ImmutableList.of();128ImmutableList<String> single = ImmutableList.of("hello");129ImmutableList<String> multi = ImmutableList.of("a", "b", "c");
  21. static <T> ImmutableList<T> of(T item)

    69static <T> ImmutableList<T> of(T itemhello) {70    return new SingleList<>(item);71}
  22. this.item ← hello

    85private T item;86SingleList(T itemhello) { this.item→ hello = item; }87@Override public int size() { return 1; }
  23. single ← ⟨SingleList E⟩

    127ImmutableList<String> empty = ImmutableList.of();128ImmutableList<String> single→ ⟨SingleList E⟩ = ImmutableList.of("hello");129ImmutableList<String> multi = ImmutableList.of("a", "b", "c");
  24. @SafeVarargs static <T> ImmutableList<T> of(T... items)

    73@SafeVarargs74static <T> ImmutableList<T> of(T... items) {75    return new ArrayBackedList<>(items.clone());76}
  25. ArrayBackedList(Object[] items)

    95private Object[] items;96ArrayBackedList(Object[] items) { this.items = items; }97@Override public int size() { return items.length; }
  26. multi ← ⟨ArrayBackedList F⟩

    128ImmutableList<String> single = ImmutableList.of("hello");129ImmutableList<String> multi→ ⟨ArrayBackedList F⟩ = ImmutableList.of("a", "b", "c");130131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));
  27. System.out.println("empty.size() = " + empty.size());

    131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputempty.size() = 0
  28. @Override public T get(int index)

    87@Override public int size() { return 1; }88@Override public T get(int index0) {89    if (index != 0) throw new IndexOutOfBoundsException();90    return itemhello;91}
  29. System.out.println("single.get(0) = " + single.get(0));

    131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputsingle.get(0) = hello
  30. @Override public int size()

    pass 1 of 5
    96ArrayBackedList(Object[] items) { this.items = items; }97@Override public int size() { return items.length3; }98@Override @SuppressWarnings("unchecked")
  31. System.out.println("multi.size() = " + multi.size());

    132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputmulti.size() = 3
  32. for (int i = 0; i < multi.size(); i++)

    pass 1 of 3
    135for (int i0 = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i0 + ") = " + multi.get(i));137}
    All 3 passes — pass 1 is the card above
    passi
    10
    21
    32
  33. @Override @SuppressWarnings("unchecked") public T get(int index)

    pass 1 of 3
    97    @Override public int size() { return items.length; }98    @Override @SuppressWarnings("unchecked")99    public T get(int index0) { return (T) items[index]a; }100}
    All 3 passes — pass 1 is the card above
    passindexitems[index]
    10a
    21b
    32c
  34. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i0 + ") = " + multi.get(i));137}
    outputmulti.get(0) = a
  35. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i1 + ") = " + multi.get(i));137}
    outputmulti.get(1) = b
  36. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i2 + ") = " + multi.get(i));137}
    outputmulti.get(2) = c
  37. System.out.println(" === Real Java Examples ===");

    139    System.out.println("\n=== Real Java Examples ===");140    System.out.println("""141        Java's static factory methods:142        - List.of("a", "b", "c")143        - Set.of(1, 2, 3)144        - Map.of("key", "value")145        - Optional.of(value)146        - Optional.empty()147        - Stream.of(items)148        - Comparator.comparing(keyExtractor)149        """);150}
    output
    === Real Java Examples ===
    Java's static factory methods:
    - List.of("a", "b", "c")
    - Set.of(1, 2, 3)
    - Map.of("key", "value")
    - Optional.of(value)
    - Optional.empty()
    - Stream.of(items)
    - Comparator.comparing(keyExtractor)
  1. circleRadius ← 8.0

    102public class FactoryMethods {103    public static void main(String[] args) {104        System.out.println("=== Shape Factory Methods ===\n");105        106        // Create shapes via interface107        double circleRadius→ 8.0 = 8.0;108        Shape circle = Shape.circle(circleRadius8.0);109        Shape rect = Shape.rectangle(4, 6);
    output=== Shape Factory Methods ===
  2. static Shape circle(double radius)

    7// Static factory methods8static Shape circle(double radius8.0) {9    return new Circle(radius);10}
  3. this.radius ← 8.0

    25Circle(double radius8.0) {26    this.radius→ 8.0 = radius8.0;27}
  4. circle ← ⟨Circle A⟩

    107double circleRadius = 8.0;108Shape circle→ ⟨Circle A⟩ = Shape.circle(circleRadius8.0);109Shape rect = Shape.rectangle(4, 6);110Shape square = Shape.square(3);
  5. static Shape rectangle(double width, double height)

    12static Shape rectangle(double width4.0, double height6.0) {13    return new Rectangle(width, height);14}
  6. this.width ← 4.0, this.height ← 6.0

    pass 1 of 2
    43Rectangle(double width4.0, double height6.0) {44    this.width→ 4.0 = width4.0;45    this.height→ 6.0 = height6.0;46}
  7. rect ← ⟨Rectangle B⟩

    108Shape circle = Shape.circle(circleRadius);109Shape rect→ ⟨Rectangle B⟩ = Shape.rectangle(4, 6);110Shape square = Shape.square(3);
  8. static Shape square(double side)

    16static Shape square(double side3.0) {17    return new Rectangle(side, side);  // Square is special rectangle18}
  9. this.width ← 3.0, this.height ← 3.0

    pass 2 of 2
    43Rectangle(double width3.0, double height3.0) {44    this.width→ 3.0 = width3.0;45    this.height→ 3.0 = height3.0;46}
  10. square ← ⟨Rectangle C⟩

    109Shape rect = Shape.rectangle(4, 6);110Shape square→ ⟨Rectangle C⟩ = Shape.square(3);111112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());
  11. @Override public String getName()

    29@Override30public String getName() {31    return "Circle(r=" + radius8.0 + ")";32}
  12. @Override public double getArea()

    34@Override35public double getArea() {36    return Math.PI * radius8.0 * radius;37}
  13. System.out.println(circle.getName() + " area: " + String.format("%.2f"…

    112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());
    outputCircle(r=8.0) area: 201.06
  14. @Override public String getName()

    pass 1 of 2
    48@Override49public String getName() {50    return "Rectangle(" + width4.0 + "x" + height6.0 + ")";51}
  15. @Override public double getArea()

    pass 1 of 2
    53@Override54public double getArea() {55    return width4.0 * height6.0;56}
  16. System.out.println(rect.getName() + " area: " + rect.getArea());

    112System.out.println(circle.getName() + " area: " + String.format("%.2f", circle.getArea()));113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());
    outputRectangle(4.0x6.0) area: 24.0
  17. @Override public String getName()

    pass 2 of 2
    48@Override49public String getName() {50    return "Rectangle(" + width3.0 + "x" + height3.0 + ")";51}
  18. @Override public double getArea()

    pass 2 of 2
    53@Override54public double getArea() {55    return width3.0 * height3.0;56}
  19. System.out.println(square.getName() + " area: " + square.getArea());

    113System.out.println(rect.getName() + " area: " + rect.getArea());114System.out.println(square.getName() + " area: " + square.getArea());115116System.out.println("\n=== Benefits of Factory Methods ===");117System.out.println("""118    1. Hide implementations - users don't know about Circle/Rectangle119    2. Return appropriate subtype - square() returns Rectangle120    3. Caching possible - can reuse instances121    4. Validation - can check parameters122    """);123124System.out.println("=== Immutable List Factory ===\n");125126// Like Java's List.of()127ImmutableList<String> empty = ImmutableList.of();128ImmutableList<String> single = ImmutableList.of("hello");
    outputRectangle(3.0x3.0) area: 9.0
    
    === Benefits of Factory Methods ===
    1. Hide implementations - users don't know about Circle/Rectangle
    2. Return appropriate subtype - square() returns Rectangle
    3. Caching possible - can reuse instances
    4. Validation - can check parameters
    === Immutable List Factory ===
  20. empty ← ⟨EmptyList D⟩

    126// Like Java's List.of()127ImmutableList<String> empty→ ⟨EmptyList D⟩ = ImmutableList.of();128ImmutableList<String> single = ImmutableList.of("hello");129ImmutableList<String> multi = ImmutableList.of("a", "b", "c");
  21. static <T> ImmutableList<T> of(T item)

    69static <T> ImmutableList<T> of(T itemhello) {70    return new SingleList<>(item);71}
  22. this.item ← hello

    85private T item;86SingleList(T itemhello) { this.item→ hello = item; }87@Override public int size() { return 1; }
  23. single ← ⟨SingleList E⟩

    127ImmutableList<String> empty = ImmutableList.of();128ImmutableList<String> single→ ⟨SingleList E⟩ = ImmutableList.of("hello");129ImmutableList<String> multi = ImmutableList.of("a", "b", "c");
  24. @SafeVarargs static <T> ImmutableList<T> of(T... items)

    73@SafeVarargs74static <T> ImmutableList<T> of(T... items) {75    return new ArrayBackedList<>(items.clone());76}
  25. ArrayBackedList(Object[] items)

    95private Object[] items;96ArrayBackedList(Object[] items) { this.items = items; }97@Override public int size() { return items.length; }
  26. multi ← ⟨ArrayBackedList F⟩

    128ImmutableList<String> single = ImmutableList.of("hello");129ImmutableList<String> multi→ ⟨ArrayBackedList F⟩ = ImmutableList.of("a", "b", "c");130131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));
  27. System.out.println("empty.size() = " + empty.size());

    131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputempty.size() = 0
  28. @Override public T get(int index)

    87@Override public int size() { return 1; }88@Override public T get(int index0) {89    if (index != 0) throw new IndexOutOfBoundsException();90    return itemhello;91}
  29. System.out.println("single.get(0) = " + single.get(0));

    131System.out.println("empty.size() = " + empty.size());132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputsingle.get(0) = hello
  30. @Override public int size()

    pass 1 of 5
    96ArrayBackedList(Object[] items) { this.items = items; }97@Override public int size() { return items.length3; }98@Override @SuppressWarnings("unchecked")
  31. System.out.println("multi.size() = " + multi.size());

    132System.out.println("single.get(0) = " + single.get(0));133System.out.println("multi.size() = " + multi.size());
    outputmulti.size() = 3
  32. for (int i = 0; i < multi.size(); i++)

    pass 1 of 3
    135for (int i0 = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i0 + ") = " + multi.get(i));137}
    All 3 passes — pass 1 is the card above
    passi
    10
    21
    32
  33. @Override @SuppressWarnings("unchecked") public T get(int index)

    pass 1 of 3
    97    @Override public int size() { return items.length; }98    @Override @SuppressWarnings("unchecked")99    public T get(int index0) { return (T) items[index]a; }100}
    All 3 passes — pass 1 is the card above
    passindexitems[index]
    10a
    21b
    32c
  34. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i0 + ") = " + multi.get(i));137}
    outputmulti.get(0) = a
  35. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i1 + ") = " + multi.get(i));137}
    outputmulti.get(1) = b
  36. System.out.println("multi.get(" + i + ") = " + multi.get(i));

    135for (int i = 0; i < multi.size(); i++) {136    System.out.println("multi.get(" + i2 + ") = " + multi.get(i));137}
    outputmulti.get(2) = c
  37. System.out.println(" === Real Java Examples ===");

    139    System.out.println("\n=== Real Java Examples ===");140    System.out.println("""141        Java's static factory methods:142        - List.of("a", "b", "c")143        - Set.of(1, 2, 3)144        - Map.of("key", "value")145        - Optional.of(value)146        - Optional.empty()147        - Stream.of(items)148        - Comparator.comparing(keyExtractor)149        """);150}
    output
    === Real Java Examples ===
    Java's static factory methods:
    - List.of("a", "b", "c")
    - Set.of(1, 2, 3)
    - Map.of("key", "value")
    - Optional.of(value)
    - Optional.empty()
    - Stream.of(items)
    - Comparator.comparing(keyExtractor)

List.of(), Map.of() are static factory methods on interfaces.

Utility methods

Helper functions related to the interface's purpose.

UtilityMethods.java
Replay: real traced execution (multi-file project)
// Utility Methods in Interfaces

interface StringUtils {
    // Null-safe operations
    static boolean isEmpty(String s) {
        return s == null || s.isEmpty();
    }

    static boolean isBlank(String s) {
        return s == null || s.isBlank();
    }

    static String nullToEmpty(String s) {
        return s == null ? "" : s;
    }

    // String transformations
    static String reverse(String s) {
        if (isEmpty(s)) return s;
        return new StringBuilder(s).reverse().toString();
    }

    static String capitalize(String s) {
        if (isEmpty(s)) return s;
        return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
    }

    static String repeat(String s, int times) {
        if (isEmpty(s) || times <= 0) return "";
        return s.repeat(times);
    }

    // Validation
    static boolean isAlpha(String s) {
        if (isEmpty(s)) return false;
        return s.chars().allMatch(Character::isLetter);
    }

    static boolean isNumeric(String s) {
        if (isEmpty(s)) return false;
        return s.chars().allMatch(Character::isDigit);
    }
}

interface ArrayUtils {
    // Array checks
    static boolean isEmpty(int[] arr) {
        return arr == null || arr.length == 0;
    }

    static boolean contains(int[] arr, int value) {
        if (isEmpty(arr)) return false;
        for (int item : arr) {
            if (item == value) return true;
        }
        return false;
    }

    // Statistics
    static int sum(int[] arr) {
        if (isEmpty(arr)) return 0;
        int total = 0;
        for (int item : arr) total += item;
        return total;
    }

    static double average(int[] arr) {
        if (isEmpty(arr)) return 0.0;
        return (double) sum(arr) / arr.length;
    }

    static int max(int[] arr) {
        if (isEmpty(arr)) throw new IllegalArgumentException("Array is empty");
        int result = arr[0];
        for (int item : arr) {
            if (item > result) result = item;
        }
        return result;
    }

    static int min(int[] arr) {
        if (isEmpty(arr)) throw new IllegalArgumentException("Array is empty");
        int result = arr[0];
        for (int item : arr) {
            if (item < result) result = item;
        }
        return result;
    }

    // Transformations
    static int[] reverse(int[] arr) {
        if (isEmpty(arr)) return arr;
        int[] result = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result[i] = arr[arr.length - 1 - i];
        }
        return result;
    }
}

public class UtilityMethods {
    public static void main(String[] args) {
        System.out.println("=== StringUtils ===\n");

        // Null safety
        System.out.println("isEmpty(null): " + StringUtils.isEmpty(null));
        System.out.println("isEmpty(\"\"): " + StringUtils.isEmpty(""));
        System.out.println("isEmpty(\"hello\"): " + StringUtils.isEmpty("hello"));
        System.out.println("isBlank(\"   \"): " + StringUtils.isBlank("   "));

        // Transformations
        System.out.println("\nreverse(\"hello\"): " + StringUtils.reverse("hello"));
        System.out.println("capitalize(\"jOHN\"): " + StringUtils.capitalize("jOHN"));
        System.out.println("repeat(\"ab\", 3): " + StringUtils.repeat("ab", 3));

        // Validation
        System.out.println("\nisAlpha(\"Hello\"): " + StringUtils.isAlpha("Hello"));
        System.out.println("isAlpha(\"Hello123\"): " + StringUtils.isAlpha("Hello123"));
        System.out.println("isNumeric(\"12345\"): " + StringUtils.isNumeric("12345"));

        System.out.println("\n=== ArrayUtils ===\n");

        int[] numbers = {5, 2, 8, 1, 9, 3};
        int[] empty = {};

        // Checks
        System.out.println("isEmpty(numbers): " + ArrayUtils.isEmpty(numbers));
        System.out.println("isEmpty(empty): " + ArrayUtils.isEmpty(empty));
        System.out.println("contains(numbers, 8): " + ArrayUtils.contains(numbers, 8));
        System.out.println("contains(numbers, 7): " + ArrayUtils.contains(numbers, 7));

        // Statistics
        System.out.println("\nsum(numbers): " + ArrayUtils.sum(numbers));
        System.out.println("average(numbers): " + ArrayUtils.average(numbers));
        System.out.println("max(numbers): " + ArrayUtils.max(numbers));
        System.out.println("min(numbers): " + ArrayUtils.min(numbers));

        // Transformations
        System.out.print("\nreverse(numbers): ");
        for (int n : ArrayUtils.reverse(numbers)) {
            System.out.print(n + " ");
        }
        System.out.println();

        System.out.println("\n=== Why Interfaces for Utilities? ===");
        System.out.println("""
            Before Java 8:
            - Utility classes with private constructor
            - All static methods
            - Couldn't use interface

            With Java 8:
            - Interfaces can have static methods
            - Cleaner organization
            - Can combine with default/abstract methods

            Benefits:
            - No need for private constructor hack
            - Cleaner than abstract class
            - Groups related utilities
            """);
    }
}
  1. public static void main(String[] args)

    101public class UtilityMethods {102    public static void main(String[] args) {103        System.out.println("=== StringUtils ===\n");104        105        // Null safety //?testnullsafe106        System.out.println("isEmpty(null): " + StringUtils.isEmpty(null));107        System.out.println("isEmpty(\"\"): " + StringUtils.isEmpty(""));
    output=== StringUtils ===
  2. static boolean isEmpty(String s)

    pass 1 of 9
    4// Null-safe operations //?nullsafe5static boolean isEmpty(String snull) {6    return snull == null || s.isEmpty();7}
    All 9 passes — pass 1 is the card above
    passs
    1null
    2(empty)
    3hello
    4hello
    5jOHN
    6ab
    7Hello
    8Hello123
    912345
  3. System.out.println("isEmpty(null): " + StringUtils.isEmpty(null));

    105// Null safety //?testnullsafe106System.out.println("isEmpty(null): " + StringUtils.isEmpty(null));107System.out.println("isEmpty(\"\"): " + StringUtils.isEmpty(""));108System.out.println("isEmpty(\"hello\"): " + StringUtils.isEmpty("hello"));
    outputisEmpty(null): true
  4. System.out.println("isEmpty(\"\"): " + StringUtils.isEmpty(""));

    106System.out.println("isEmpty(null): " + StringUtils.isEmpty(null));107System.out.println("isEmpty(\"\"): " + StringUtils.isEmpty(""));108System.out.println("isEmpty(\"hello\"): " + StringUtils.isEmpty("hello"));109System.out.println("isBlank(\"   \"): " + StringUtils.isBlank("   "));
    outputisEmpty(""): true
  5. System.out.println("isEmpty(\"hello\"): " + StringUtils.isEmpty("hello…

    107System.out.println("isEmpty(\"\"): " + StringUtils.isEmpty(""));108System.out.println("isEmpty(\"hello\"): " + StringUtils.isEmpty("hello"));109System.out.println("isBlank(\"   \"): " + StringUtils.isBlank("   "));
    outputisEmpty("hello"): false
  6. static boolean isBlank(String s)

    9static boolean isBlank(String s   ) {10    return s    == null || s.isBlank();11}
  7. System.out.println("isBlank(\" \"): " + StringUtils.isBlank(" "));

    108System.out.println("isEmpty(\"hello\"): " + StringUtils.isEmpty("hello"));109System.out.println("isBlank(\"   \"): " + StringUtils.isBlank("   "));110111// Transformations //?testtransform112System.out.println("\nreverse(\"hello\"): " + StringUtils.reverse("hello"));113System.out.println("capitalize(\"jOHN\"): " + StringUtils.capitalize("jOHN"));
    outputisBlank("   "): true
  8. static String reverse(String s)

    17// String transformations //?transformations18static String reverse(String shello) {19    if (isEmpty(s)) return s;
  9. return new StringBuilder(s).reverse().toString();

    19    if (isEmpty(s)) return s;20    return new StringBuilder(s).reverse().toString();21}
  10. System.out.println(" reverse(\"hello\"): " + StringUtils.reverse("hell…

    111// Transformations //?testtransform112System.out.println("\nreverse(\"hello\"): " + StringUtils.reverse("hello"));113System.out.println("capitalize(\"jOHN\"): " + StringUtils.capitalize("jOHN"));114System.out.println("repeat(\"ab\", 3): " + StringUtils.repeat("ab", 3));
    output
    reverse("hello"): olleh
  11. static String capitalize(String s)

    23static String capitalize(String sjOHN) {24    if (isEmpty(s)) return s;
  12. return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase…

    24    if (isEmpty(s)) return s;25    return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();26}
  13. System.out.println("capitalize(\"jOHN\"): " + StringUtils.capitalize("…

    112System.out.println("\nreverse(\"hello\"): " + StringUtils.reverse("hello"));113System.out.println("capitalize(\"jOHN\"): " + StringUtils.capitalize("jOHN"));114System.out.println("repeat(\"ab\", 3): " + StringUtils.repeat("ab", 3));
    outputcapitalize("jOHN"): John
  14. static String repeat(String s, int times)

    28static String repeat(String sab, int times3) { //?repeat29    if (isEmpty(s) || times <= 0) return "";
  15. return s.repeat(times);

    29    if (isEmpty(s) || times <= 0) return "";30    return s.repeat(times3);31}
  16. System.out.println("repeat(\"ab\", 3): " + StringUtils.repeat("ab", 3)…

    113System.out.println("capitalize(\"jOHN\"): " + StringUtils.capitalize("jOHN"));114System.out.println("repeat(\"ab\", 3): " + StringUtils.repeat("ab", 3));115116// Validation //?testvalidation117System.out.println("\nisAlpha(\"Hello\"): " + StringUtils.isAlpha("Hello"));118System.out.println("isAlpha(\"Hello123\"): " + StringUtils.isAlpha("Hello123"));
    outputrepeat("ab", 3): ababab
  17. static boolean isAlpha(String s)

    pass 1 of 2
    33// Validation //?validation34static boolean isAlpha(String sHello) {35    if (isEmpty(s)) return false;
  18. return s.chars().allMatch(Character::isLetter);

    35    if (isEmpty(s)) return false;36    return s.chars().allMatch(Character::isLetter);37}
  19. System.out.println(" isAlpha(\"Hello\"): " + StringUtils.isAlpha("Hell…

    116// Validation //?testvalidation117System.out.println("\nisAlpha(\"Hello\"): " + StringUtils.isAlpha("Hello"));118System.out.println("isAlpha(\"Hello123\"): " + StringUtils.isAlpha("Hello123"));119System.out.println("isNumeric(\"12345\"): " + StringUtils.isNumeric("12345"));
    output
    isAlpha("Hello"): true
  20. static boolean isAlpha(String s)

    pass 2 of 2
    33// Validation //?validation34static boolean isAlpha(String sHello123) {35    if (isEmpty(s)) return false;
  21. return s.chars().allMatch(Character::isLetter);

    35    if (isEmpty(s)) return false;36    return s.chars().allMatch(Character::isLetter);37}
  22. System.out.println("isAlpha(\"Hello123\"): " + StringUtils.isAlpha("He…

    117System.out.println("\nisAlpha(\"Hello\"): " + StringUtils.isAlpha("Hello"));118System.out.println("isAlpha(\"Hello123\"): " + StringUtils.isAlpha("Hello123"));119System.out.println("isNumeric(\"12345\"): " + StringUtils.isNumeric("12345"));
    outputisAlpha("Hello123"): false
  23. static boolean isNumeric(String s)

    39static boolean isNumeric(String s12345) {40    if (isEmpty(s)) return false;
  24. return s.chars().allMatch(Character::isDigit);

    40    if (isEmpty(s)) return false;41    return s.chars().allMatch(Character::isDigit);42}
  25. int[] numbers = {5, 2, 8, 1, 9, 3};

    118System.out.println("isAlpha(\"Hello123\"): " + StringUtils.isAlpha("Hello123"));119System.out.println("isNumeric(\"12345\"): " + StringUtils.isNumeric("12345"));120121System.out.println("\n=== ArrayUtils ===\n");122123int[] numbers = {5, 2, 8, 1, 9, 3};124int[] empty = {};125126// Checks //?testchecks127System.out.println("isEmpty(numbers): " + ArrayUtils.isEmpty(numbers));128System.out.println("isEmpty(empty): " + ArrayUtils.isEmpty(empty));
    outputisNumeric("12345"): true
    
    === ArrayUtils ===
  26. static boolean isEmpty(int[] arr)

    pass 1 of 10
    46// Array checks //?arraychecks47static boolean isEmpty(int[] arr) {48    return arr == null || arr.length6 == 0;49}
    All 10 passes — pass 1 is the card above
    passarr.lengthitemvalue
    16
    20
    3688
    46
    56
    66
    76
    86
    96
    106
  27. System.out.println("isEmpty(numbers): " + ArrayUtils.isEmpty(numbers))…

    126// Checks //?testchecks127System.out.println("isEmpty(numbers): " + ArrayUtils.isEmpty(numbers));128System.out.println("isEmpty(empty): " + ArrayUtils.isEmpty(empty));129System.out.println("contains(numbers, 8): " + ArrayUtils.contains(numbers, 8));
    outputisEmpty(numbers): false
  28. System.out.println("isEmpty(empty): " + ArrayUtils.isEmpty(empty));

    127System.out.println("isEmpty(numbers): " + ArrayUtils.isEmpty(numbers));128System.out.println("isEmpty(empty): " + ArrayUtils.isEmpty(empty));129System.out.println("contains(numbers, 8): " + ArrayUtils.contains(numbers, 8));130System.out.println("contains(numbers, 7): " + ArrayUtils.contains(numbers, 7));
    outputisEmpty(empty): true
  29. static boolean contains(int[] arr, int value)

    pass 1 of 2
    51static boolean contains(int[] arr, int value8) { //?contains52    if (isEmpty(arr)) return false;
  30. for (int item : arr)

    pass 1 of 9
    52if (isEmpty(arr)) return false;53for (int item5 : arr) {54    if (item == value) return true;
    All 9 passes — pass 1 is the card above
    passitemvalue
    15
    22
    388
    45
    52
    68
    71
    89
    93
  31. if (item == value)

    53for (int item : arr) {54    if (item8 == value8) return true;55}
  32. System.out.println("contains(numbers, 8): " + ArrayUtils.contains(numb…

    128System.out.println("isEmpty(empty): " + ArrayUtils.isEmpty(empty));129System.out.println("contains(numbers, 8): " + ArrayUtils.contains(numbers, 8));130System.out.println("contains(numbers, 7): " + ArrayUtils.contains(numbers, 7));
    outputcontains(numbers, 8): true
  33. static boolean contains(int[] arr, int value)

    pass 2 of 2
    51static boolean contains(int[] arr, int value7) { //?contains52    if (isEmpty(arr)) return false;
  34. return false;

    55    }56    return false;57}
  35. System.out.println("contains(numbers, 7): " + ArrayUtils.contains(numb…

    129System.out.println("contains(numbers, 8): " + ArrayUtils.contains(numbers, 8));130System.out.println("contains(numbers, 7): " + ArrayUtils.contains(numbers, 7));131132// Statistics //?teststatistics133System.out.println("\nsum(numbers): " + ArrayUtils.sum(numbers));134System.out.println("average(numbers): " + ArrayUtils.average(numbers));
    outputcontains(numbers, 7): false
  36. static int sum(int[] arr)

    pass 1 of 2
    59// Statistics //?statistics60static int sum(int[] arr) {61    if (isEmpty(arr)) return 0;
  37. total ← 0

    61if (isEmpty(arr)) return 0;62int total→ 0 = 0;63for (int item : arr) total += item;
  38. total ← 5

    pass 1 of 12
    62int total = 0;63for (int item5 : arr) total→ 5 += item;64return total;
    All 12 passes — pass 1 is the card above
    passitemtotal
    150 5
    225 7
    387 15
    4115 16
    5916 25
    6325 28
    750 5
    825 7
    987 15
    10115 16
    11916 25
    12325 28
  39. return total;

    63    for (int item : arr) total += item;64    return total28;65}
  40. System.out.println(" sum(numbers): " + ArrayUtils.sum(numbers));

    132// Statistics //?teststatistics133System.out.println("\nsum(numbers): " + ArrayUtils.sum(numbers));134System.out.println("average(numbers): " + ArrayUtils.average(numbers));135System.out.println("max(numbers): " + ArrayUtils.max(numbers));
    output
    sum(numbers): 28
  41. static double average(int[] arr)

    67static double average(int[] arr) { //?average68    if (isEmpty(arr)) return 0.0;
  42. return (double) sum(arr) / arr.length;

    68    if (isEmpty(arr)) return 0.0;69    return (double) sum(arr) / arr.length6;70}
  43. static int sum(int[] arr)

    pass 2 of 2
    59// Statistics //?statistics60static int sum(int[] arr) {61    if (isEmpty(arr)) return 0;
  44. total ← 0

    61if (isEmpty(arr)) return 0;62int total→ 0 = 0;63for (int item : arr) total += item;
  45. return total;

    63    for (int item : arr) total += item;64    return total28;65}
  46. System.out.println("average(numbers): " + ArrayUtils.average(numbers))…

    133System.out.println("\nsum(numbers): " + ArrayUtils.sum(numbers));134System.out.println("average(numbers): " + ArrayUtils.average(numbers));135System.out.println("max(numbers): " + ArrayUtils.max(numbers));136System.out.println("min(numbers): " + ArrayUtils.min(numbers));
    outputaverage(numbers): 4.666666666666667
  47. static int max(int[] arr)

    72static int max(int[] arr) { //?max73    if (isEmpty(arr)) throw new IllegalArgumentException("Array is empty");
  48. result ← 5

    73if (isEmpty(arr)) throw new IllegalArgumentException("Array is empty");74int result→ 5 = arr[0]5;75for (int item : arr) {
  49. for (int item : arr)

    pass 1 of 6
    74int result = arr[0];75for (int item5 : arr) {76    if (item > result) result = item;
    All 6 passes — pass 1 is the card above
    passitemresult
    15
    22
    385 8
    41
    598 9
    63
  50. result ← 8

    pass 1 of 2
    75for (int item : arr) {76    if (item8 > result5) result = item;77}
  51. result ← 9

    pass 2 of 2
    75for (int item : arr) {76    if (item9 > result8) result = item;77}
  52. return result;

    77    }78    return result9;79}
  53. System.out.println("max(numbers): " + ArrayUtils.max(numbers));

    134System.out.println("average(numbers): " + ArrayUtils.average(numbers));135System.out.println("max(numbers): " + ArrayUtils.max(numbers));136System.out.println("min(numbers): " + ArrayUtils.min(numbers));
    outputmax(numbers): 9
  54. static int min(int[] arr)

    81static int min(int[] arr) {82    if (isEmpty(arr)) throw new IllegalArgumentException("Array is empty");
  55. result ← 5

    82if (isEmpty(arr)) throw new IllegalArgumentException("Array is empty");83int result→ 5 = arr[0]5;84for (int item : arr) {
  56. for (int item : arr)

    pass 1 of 6
    83int result = arr[0];84for (int item5 : arr) {85    if (item < result) result = item;
    All 6 passes — pass 1 is the card above
    passitemresult
    15
    225 2
    38
    412 1
    59
    63
  57. result ← 2

    pass 1 of 2
    84for (int item : arr) {85    if (item2 < result5) result = item;86}
  58. result ← 1

    pass 2 of 2
    84for (int item : arr) {85    if (item1 < result2) result = item;86}
  59. return result;

    86    }87    return result1;88}
  60. System.out.println("min(numbers): " + ArrayUtils.min(numbers));

    135System.out.println("max(numbers): " + ArrayUtils.max(numbers));136System.out.println("min(numbers): " + ArrayUtils.min(numbers));137138// Transformations //?testreverse139System.out.print("\nreverse(numbers): ");140for (int n : ArrayUtils.reverse(numbers)) {
    outputmin(numbers): 1
    
    reverse(numbers): 
  61. static int[] reverse(int[] arr)

    90// Transformations //?arraytransform91static int[] reverse(int[] arr) {92    if (isEmpty(arr)) return arr;
  62. int[] result = new int[arr.length];

    92if (isEmpty(arr)) return arr;93int[] result = new int[arr.length6];94for (int i = 0; i < arr.length; i++) {
  63. result[i] ← 3

    pass 1 of 6
    93int[] result = new int[arr.length];94for (int i0 = 0; i < arr.length6; i++) {95    result[i]→ 3 = arr[arr.length - 1 - i]3;96}
    All 6 passes — pass 1 is the card above
    passiarr[arr.length - 1 - i]result[i]
    1030 3
    2190 9
    3210 1
    4380 8
    5420 2
    6550 5
  64. return result;

    96    }97    return result;98}
  65. for (int n : ArrayUtils.reverse(numbers))

    pass 1 of 6
    139System.out.print("\nreverse(numbers): ");140for (int n3 : ArrayUtils.reverse(numbers)) {141    System.out.print(n3 + " ");142}
    output3 
    All 6 passes — pass 1 is the card above
    passn
    13
    29
    31
    48
    52
    65
  66. System.out.println();

    142    }143    System.out.println();144    145    System.out.println("\n=== Why Interfaces for Utilities? ===");146    System.out.println("""147        Before Java 8:148        - Utility classes with private constructor149        - All static methods150        - Couldn't use interface151        152        With Java 8:153        - Interfaces can have static methods154        - Cleaner organization155        - Can combine with default/abstract methods156        157        Benefits:158        - No need for private constructor hack159        - Cleaner than abstract class160        - Groups related utilities161        """);162}
    output
    === Why Interfaces for Utilities? ===
    Before Java 8:
    - Utility classes with private constructor
    - All static methods
    - Couldn't use interface
    
    With Java 8:
    - Interfaces can have static methods
    - Cleaner organization
    - Can combine with default/abstract methods
    
    Benefits:
    - No need for private constructor hack
    - Cleaner than abstract class
    - Groups related utilities

Group related utilities on the interface instead of separate helper class.

Static with default

Combine static and default methods in one interface.

minChars
StaticWithDefault.java
Replay: real traced execution (multi-file project)
// Combining Static and Default Methods

interface Validator<T> {
    // Abstract - each validator implements differently
    boolean isValid(T value);
    String getErrorMessage();

    // Default - common behavior
    default ValidationResult validate(T value) {
        if (isValid(value)) {
            return ValidationResult.success();
        }
        return ValidationResult.failure(getErrorMessage());
    }

    // Static factory methods for common validators
    static Validator<String> notEmpty() {
        return new Validator<>() {
            @Override
            public boolean isValid(String value) {
                return value != null && !value.isEmpty();
            }

            @Override
            public String getErrorMessage() {
                return "Value cannot be empty";
            }
        };
    }

    static Validator<String> minLength(int min) {
        return new Validator<>() {
            @Override
            public boolean isValid(String value) {
                return value != null && value.length() >= min;
            }

            @Override
            public String getErrorMessage() {
                return "Value must be at least " + min + " characters";
            }
        };
    }

    static Validator<Integer> range(int min, int max) {
        return new Validator<>() {
            @Override
            public boolean isValid(Integer value) {
                return value != null && value >= min && value <= max;
            }

            @Override
            public String getErrorMessage() {
                return "Value must be between " + min + " and " + max;
            }
        };
    }

    // Static combinator methods
    static <T> Validator<T> and(Validator<T> v1, Validator<T> v2) {
        return new Validator<>() {
            @Override
            public boolean isValid(T value) {
                return v1.isValid(value) && v2.isValid(value);
            }

            @Override
            public String getErrorMessage() {
                return v1.getErrorMessage() + " AND " + v2.getErrorMessage();
            }
        };
    }
}

// Simple result class
class ValidationResult {
    private final boolean valid;
    private final String message;

    private ValidationResult(boolean valid, String message) {
        this.valid = valid;
        this.message = message;
    }

    static ValidationResult success() {
        return new ValidationResult(true, "OK");
    }

    static ValidationResult failure(String message) {
        return new ValidationResult(false, message);
    }

    @Override
    public String toString() {
        return valid ? "✓ Valid" : "✗ Invalid: " + message;
    }
}

// Custom validator
class EmailValidator implements Validator<String> {
    @Override
    public boolean isValid(String value) {
        return value != null && value.contains("@") && value.contains(".");
    }

    @Override
    public String getErrorMessage() {
        return "Invalid email format";
    }
}

public class StaticWithDefault {
    public static void main(String[] args) {
        System.out.println("=== Validator Interface ===\n");

        // Using static factory methods
        Validator<String> notEmpty = Validator.notEmpty();
        int minChars = 5;
        Validator<String> minLengthRule = Validator.minLength(minChars);
        Validator<Integer> ageRange = Validator.range(18, 65);

        System.out.println("--- notEmpty validator ---");
        System.out.println("\"hello\": " + notEmpty.validate("hello"));
        System.out.println("\"\": " + notEmpty.validate(""));
        System.out.println("null: " + notEmpty.validate(null));

        System.out.println("\n--- minLength(" + minChars + ") validator ---");
        System.out.println("\"hello\": " + minLengthRule.validate("hello"));
        System.out.println("\"hi\": " + minLengthRule.validate("hi"));

        System.out.println("\n--- range(18, 65) validator ---");
        System.out.println("25: " + ageRange.validate(25));
        System.out.println("15: " + ageRange.validate(15));
        System.out.println("70: " + ageRange.validate(70));

        // Using combinator
        System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");
        Validator<String> combined = Validator.and(notEmpty, minLengthRule);
        System.out.println("\"hello world\": " + combined.validate("hello world"));
        System.out.println("\"hi\": " + combined.validate("hi"));
        System.out.println("\"\": " + combined.validate(""));

        // Custom validator uses default method
        System.out.println("\n--- Custom EmailValidator ---");
        Validator<String> email = new EmailValidator();
        System.out.println("\"test@example.com\": " + email.validate("test@example.com"));
        System.out.println("\"invalid-email\": " + email.validate("invalid-email"));

        System.out.println("\n=== Design Summary ===");
        System.out.println("""
            Static methods:
            - Validator.notEmpty() - factory for common validator
            - Validator.minLength(n) - configurable factory
            - Validator.range(min, max) - another factory
            - Validator.and(v1, v2) - combinator

            Default method:
            - validate(value) - uses isValid() and getErrorMessage()

            Abstract methods:
            - isValid() - must implement
            - getErrorMessage() - must implement

            This pattern:
            - Factories create pre-built validators
            - Custom validators implement interface
            - All get validate() for free
            """);
    }
}
// Combining Static and Default Methods

interface Validator<T> {
    // Abstract - each validator implements differently
    boolean isValid(T value);
    String getErrorMessage();

    // Default - common behavior
    default ValidationResult validate(T value) {
        if (isValid(value)) {
            return ValidationResult.success();
        }
        return ValidationResult.failure(getErrorMessage());
    }

    // Static factory methods for common validators
    static Validator<String> notEmpty() {
        return new Validator<>() {
            @Override
            public boolean isValid(String value) {
                return value != null && !value.isEmpty();
            }

            @Override
            public String getErrorMessage() {
                return "Value cannot be empty";
            }
        };
    }

    static Validator<String> minLength(int min) {
        return new Validator<>() {
            @Override
            public boolean isValid(String value) {
                return value != null && value.length() >= min;
            }

            @Override
            public String getErrorMessage() {
                return "Value must be at least " + min + " characters";
            }
        };
    }

    static Validator<Integer> range(int min, int max) {
        return new Validator<>() {
            @Override
            public boolean isValid(Integer value) {
                return value != null && value >= min && value <= max;
            }

            @Override
            public String getErrorMessage() {
                return "Value must be between " + min + " and " + max;
            }
        };
    }

    // Static combinator methods
    static <T> Validator<T> and(Validator<T> v1, Validator<T> v2) {
        return new Validator<>() {
            @Override
            public boolean isValid(T value) {
                return v1.isValid(value) && v2.isValid(value);
            }

            @Override
            public String getErrorMessage() {
                return v1.getErrorMessage() + " AND " + v2.getErrorMessage();
            }
        };
    }
}

// Simple result class
class ValidationResult {
    private final boolean valid;
    private final String message;

    private ValidationResult(boolean valid, String message) {
        this.valid = valid;
        this.message = message;
    }

    static ValidationResult success() {
        return new ValidationResult(true, "OK");
    }

    static ValidationResult failure(String message) {
        return new ValidationResult(false, message);
    }

    @Override
    public String toString() {
        return valid ? "✓ Valid" : "✗ Invalid: " + message;
    }
}

// Custom validator
class EmailValidator implements Validator<String> {
    @Override
    public boolean isValid(String value) {
        return value != null && value.contains("@") && value.contains(".");
    }

    @Override
    public String getErrorMessage() {
        return "Invalid email format";
    }
}

public class StaticWithDefault {
    public static void main(String[] args) {
        System.out.println("=== Validator Interface ===\n");

        // Using static factory methods
        Validator<String> notEmpty = Validator.notEmpty();
        int minChars = 3;
        Validator<String> minLengthRule = Validator.minLength(minChars);
        Validator<Integer> ageRange = Validator.range(18, 65);

        System.out.println("--- notEmpty validator ---");
        System.out.println("\"hello\": " + notEmpty.validate("hello"));
        System.out.println("\"\": " + notEmpty.validate(""));
        System.out.println("null: " + notEmpty.validate(null));

        System.out.println("\n--- minLength(" + minChars + ") validator ---");
        System.out.println("\"hello\": " + minLengthRule.validate("hello"));
        System.out.println("\"hi\": " + minLengthRule.validate("hi"));

        System.out.println("\n--- range(18, 65) validator ---");
        System.out.println("25: " + ageRange.validate(25));
        System.out.println("15: " + ageRange.validate(15));
        System.out.println("70: " + ageRange.validate(70));

        // Using combinator
        System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");
        Validator<String> combined = Validator.and(notEmpty, minLengthRule);
        System.out.println("\"hello world\": " + combined.validate("hello world"));
        System.out.println("\"hi\": " + combined.validate("hi"));
        System.out.println("\"\": " + combined.validate(""));

        // Custom validator uses default method
        System.out.println("\n--- Custom EmailValidator ---");
        Validator<String> email = new EmailValidator();
        System.out.println("\"test@example.com\": " + email.validate("test@example.com"));
        System.out.println("\"invalid-email\": " + email.validate("invalid-email"));

        System.out.println("\n=== Design Summary ===");
        System.out.println("""
            Static methods:
            - Validator.notEmpty() - factory for common validator
            - Validator.minLength(n) - configurable factory
            - Validator.range(min, max) - another factory
            - Validator.and(v1, v2) - combinator

            Default method:
            - validate(value) - uses isValid() and getErrorMessage()

            Abstract methods:
            - isValid() - must implement
            - getErrorMessage() - must implement

            This pattern:
            - Factories create pre-built validators
            - Custom validators implement interface
            - All get validate() for free
            """);
    }
}
// Combining Static and Default Methods

interface Validator<T> {
    // Abstract - each validator implements differently
    boolean isValid(T value);
    String getErrorMessage();

    // Default - common behavior
    default ValidationResult validate(T value) {
        if (isValid(value)) {
            return ValidationResult.success();
        }
        return ValidationResult.failure(getErrorMessage());
    }

    // Static factory methods for common validators
    static Validator<String> notEmpty() {
        return new Validator<>() {
            @Override
            public boolean isValid(String value) {
                return value != null && !value.isEmpty();
            }

            @Override
            public String getErrorMessage() {
                return "Value cannot be empty";
            }
        };
    }

    static Validator<String> minLength(int min) {
        return new Validator<>() {
            @Override
            public boolean isValid(String value) {
                return value != null && value.length() >= min;
            }

            @Override
            public String getErrorMessage() {
                return "Value must be at least " + min + " characters";
            }
        };
    }

    static Validator<Integer> range(int min, int max) {
        return new Validator<>() {
            @Override
            public boolean isValid(Integer value) {
                return value != null && value >= min && value <= max;
            }

            @Override
            public String getErrorMessage() {
                return "Value must be between " + min + " and " + max;
            }
        };
    }

    // Static combinator methods
    static <T> Validator<T> and(Validator<T> v1, Validator<T> v2) {
        return new Validator<>() {
            @Override
            public boolean isValid(T value) {
                return v1.isValid(value) && v2.isValid(value);
            }

            @Override
            public String getErrorMessage() {
                return v1.getErrorMessage() + " AND " + v2.getErrorMessage();
            }
        };
    }
}

// Simple result class
class ValidationResult {
    private final boolean valid;
    private final String message;

    private ValidationResult(boolean valid, String message) {
        this.valid = valid;
        this.message = message;
    }

    static ValidationResult success() {
        return new ValidationResult(true, "OK");
    }

    static ValidationResult failure(String message) {
        return new ValidationResult(false, message);
    }

    @Override
    public String toString() {
        return valid ? "✓ Valid" : "✗ Invalid: " + message;
    }
}

// Custom validator
class EmailValidator implements Validator<String> {
    @Override
    public boolean isValid(String value) {
        return value != null && value.contains("@") && value.contains(".");
    }

    @Override
    public String getErrorMessage() {
        return "Invalid email format";
    }
}

public class StaticWithDefault {
    public static void main(String[] args) {
        System.out.println("=== Validator Interface ===\n");

        // Using static factory methods
        Validator<String> notEmpty = Validator.notEmpty();
        int minChars = 8;
        Validator<String> minLengthRule = Validator.minLength(minChars);
        Validator<Integer> ageRange = Validator.range(18, 65);

        System.out.println("--- notEmpty validator ---");
        System.out.println("\"hello\": " + notEmpty.validate("hello"));
        System.out.println("\"\": " + notEmpty.validate(""));
        System.out.println("null: " + notEmpty.validate(null));

        System.out.println("\n--- minLength(" + minChars + ") validator ---");
        System.out.println("\"hello\": " + minLengthRule.validate("hello"));
        System.out.println("\"hi\": " + minLengthRule.validate("hi"));

        System.out.println("\n--- range(18, 65) validator ---");
        System.out.println("25: " + ageRange.validate(25));
        System.out.println("15: " + ageRange.validate(15));
        System.out.println("70: " + ageRange.validate(70));

        // Using combinator
        System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");
        Validator<String> combined = Validator.and(notEmpty, minLengthRule);
        System.out.println("\"hello world\": " + combined.validate("hello world"));
        System.out.println("\"hi\": " + combined.validate("hi"));
        System.out.println("\"\": " + combined.validate(""));

        // Custom validator uses default method
        System.out.println("\n--- Custom EmailValidator ---");
        Validator<String> email = new EmailValidator();
        System.out.println("\"test@example.com\": " + email.validate("test@example.com"));
        System.out.println("\"invalid-email\": " + email.validate("invalid-email"));

        System.out.println("\n=== Design Summary ===");
        System.out.println("""
            Static methods:
            - Validator.notEmpty() - factory for common validator
            - Validator.minLength(n) - configurable factory
            - Validator.range(min, max) - another factory
            - Validator.and(v1, v2) - combinator

            Default method:
            - validate(value) - uses isValid() and getErrorMessage()

            Abstract methods:
            - isValid() - must implement
            - getErrorMessage() - must implement

            This pattern:
            - Factories create pre-built validators
            - Custom validators implement interface
            - All get validate() for free
            """);
    }
}
  1. public static void main(String[] args)

    112public class StaticWithDefault {113    public static void main(String[] args) {114        System.out.println("=== Validator Interface ===\n");115        116        // Using static factory methods //?usestatic117        Validator<String> notEmpty = Validator.notEmpty();118        int minChars = 5;  //@minChars=5, 3, 8
    output=== Validator Interface ===
  2. notEmpty ← ⟨Validator$1 A⟩, minChars ← 5

    116// Using static factory methods //?usestatic117Validator<String> notEmpty→ ⟨Validator$1 A⟩ = Validator.notEmpty();118int minChars→ 5 = 5;  //@minChars=5, 3, 8119Validator<String> minLengthRule = Validator.minLength(minChars5);120Validator<Integer> ageRange = Validator.range(18, 65);
  3. static Validator<String> minLength(int min)

    31static Validator<String> minLength(int min5) { //?minlength32    return new Validator<>() {33        @Override34        public boolean isValid(String value) {35            return value != null && value.length() >= min;36        }37        38        @Override39        public String getErrorMessage() {40            return "Value must be at least " + min + " characters";41        }42    };43}
  4. minLengthRule ← ⟨Validator$2 B⟩

    118int minChars = 5;  //@minChars=5, 3, 8119Validator<String> minLengthRule→ ⟨Validator$2 B⟩ = Validator.minLength(minChars5);120Validator<Integer> ageRange = Validator.range(18, 65);
  5. static Validator<Integer> range(int min, int max)

    45static Validator<Integer> range(int min18, int max65) { //?range46    return new Validator<>() {47        @Override48        public boolean isValid(Integer value) {49            return value != null && value >= min && value <= max;50        }51        52        @Override53        public String getErrorMessage() {54            return "Value must be between " + min + " and " + max;55        }56    };57}
  6. ageRange ← ⟨Validator$3 C⟩

    119Validator<String> minLengthRule = Validator.minLength(minChars);120Validator<Integer> ageRange→ ⟨Validator$3 C⟩ = Validator.range(18, 65);121122System.out.println("--- notEmpty validator ---");123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));
    output--- notEmpty validator ---
  7. default ValidationResult validate(T value)

    pass 1 of 13
    8// Default - common behavior //?defaultvalidate9default ValidationResult validate(T valuehello) {10    if (isValid(value)) {
    13 passes — pass 1 is the card above
    passvalue
    1hello
    2(empty)
    3null
    4hello
    5hi
    625
    715
    870
    9hello world
    ⋯ 2 more passes ⋯
    12test@example.com
    13invalid-email
  8. @Override public boolean isValid(String value)

    pass 1 of 6
    18return new Validator<>() {19    @Override20    public boolean isValid(String valuehello) {21        return valuehello != null && !value.isEmpty();22    }
    All 6 passes — pass 1 is the card above
    passvalue
    1hello
    2(empty)
    3null
    4hello world
    5hi
    6(empty)
  9. if (isValid(value))

    pass 1 of 5
    9default ValidationResult validate(T value) {10    if (isValid(valuehello)) {11        return ValidationResult.success();12    }
    All 5 passes — pass 1 is the card above
    passvalue
    1hello
    2hello
    325
    4hello world
    5test@example.com
  10. this.valid ← true, this.message ← OK

    pass 1 of 13
    80private ValidationResult(boolean validtrue, String messageOK) {81    this.valid→ true = validtrue;82    this.message→ OK = messageOK;83}
    13 passes — pass 1 is the card above
    passvalidmessagethis.validthis.message
    1trueOKtrueOK
    2falseValue cannot be emptyfalseValue cannot be empty
    3falseValue cannot be emptyfalseValue cannot be empty
    4trueOKtrueOK
    5falseValue must be at least 5 charactersfalseValue must be at least 5 characters
    6trueOKtrueOK
    7falseValue must be between 18 and 65falseValue must be between 18 and 65
    8falseValue must be between 18 and 65falseValue must be between 18 and 65
    9trueOKtrueOK
    ⋯ 2 more passes ⋯
    12trueOKtrueOK
    13falseInvalid email formatfalseInvalid email format
  11. System.out.println("\"hello\": " + notEmpty.validate("hello"));

    122System.out.println("--- notEmpty validator ---");123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));
    output"hello": ✓ Valid
  12. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  13. static ValidationResult failure(String message)

    pass 1 of 8
    89static ValidationResult failure(String messageValue cannot be empty) {90    return new ValidationResult(false, message);91}
    All 8 passes — pass 1 is the card above
    passmessage
    1Value cannot be empty
    2Value cannot be empty
    3Value must be at least 5 characters
    4Value must be between 18 and 65
    5Value must be between 18 and 65
    6Value cannot be empty AND Value must be at least 5 characters
    7Value cannot be empty AND Value must be at least 5 characters
    8Invalid email format
  14. System.out.println("\"\": " + notEmpty.validate(""));

    123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));
    output"": ✗ Invalid: Value cannot be empty
  15. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  16. System.out.println(" --- minLength(" + minChars + ") validator ---");

    124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));126127System.out.println("\n--- minLength(" + minChars5 + ") validator ---");128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));
    outputnull: ✗ Invalid: Value cannot be empty
    
    --- minLength(5) validator ---
  17. @Override public boolean isValid(String value)

    pass 1 of 4
    32return new Validator<>() {33    @Override34    public boolean isValid(String valuehello) {35        return valuehello != null && value.length() >= min5;36    }
    All 4 passes — pass 1 is the card above
    passvalue
    1hello
    2hi
    3hello world
    4hi
  18. System.out.println("\"hello\": " + minLengthRule.validate("hello"));

    127System.out.println("\n--- minLength(" + minChars + ") validator ---");128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));
    output"hello": ✓ Valid
  19. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  20. @Override public String getErrorMessage()

    pass 1 of 3
    38@Override39public String getErrorMessage() {40    return "Value must be at least " + min5 + " characters";41}
  21. System.out.println("\"hi\": " + minLengthRule.validate("hi"));

    128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));130131System.out.println("\n--- range(18, 65) validator ---");132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));
    output"hi": ✗ Invalid: Value must be at least 5 characters
    
    --- range(18, 65) validator ---
  22. @Override public boolean isValid(Integer value)

    pass 1 of 3
    46return new Validator<>() {47    @Override48    public boolean isValid(Integer value25) {49        return value25 != null && value >= min18 && value <= max65;50    }
    All 3 passes — pass 1 is the card above
    passvalue
    125
    215
    370
  23. System.out.println("25: " + ageRange.validate(25));

    131System.out.println("\n--- range(18, 65) validator ---");132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));
    output25: ✓ Valid
  24. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  25. @Override public String getErrorMessage()

    pass 1 of 2
    52@Override53public String getErrorMessage() {54    return "Value must be between " + min18 + " and " + max65;55}
  26. System.out.println("15: " + ageRange.validate(15));

    132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));
    output15: ✗ Invalid: Value must be between 18 and 65
  27. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  28. @Override public String getErrorMessage()

    pass 2 of 2
    52@Override53public String getErrorMessage() {54    return "Value must be between " + min18 + " and " + max65;55}
  29. Validator<String> combined = Validator.and(notEmpty, minLengthRule);

    133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));135136// Using combinator //?usecombinator137System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");138Validator<String> combined = Validator.and(notEmpty⟨Validator$1 A⟩, minLengthRule⟨Validator$2 B⟩);139System.out.println("\"hello world\": " + combined.validate("hello world"));
    output70: ✗ Invalid: Value must be between 18 and 65
    
    --- Combined validator (notEmpty AND minLength) ---
  30. static <T> Validator<T> and(Validator<T> v1, Validator<T> v2)

    59// Static combinator methods //?combinators60static <T> Validator<T> and(Validator<T> v1⟨Validator$1 A⟩, Validator<T> v2⟨Validator$2 B⟩) { //?andcombinator61    return new Validator<>() {62        @Override63        public boolean isValid(T value) {64            return v1.isValid(value) && v2.isValid(value);65        }66        67        @Override68        public String getErrorMessage() {69            return v1.getErrorMessage() + " AND " + v2.getErrorMessage();70        }71    };72}
  31. combined ← ⟨Validator$4 D⟩

    137System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");138Validator<String> combined→ ⟨Validator$4 D⟩ = Validator.and(notEmpty⟨Validator$1 A⟩, minLengthRule⟨Validator$2 B⟩);139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));
  32. @Override public boolean isValid(T value)

    pass 1 of 3
    61return new Validator<>() {62    @Override63    public boolean isValid(T valuehello world) {64        return v1.isValid(valuehello world) && v2.isValid(value);65    }
    All 3 passes — pass 1 is the card above
    passvalue
    1hello world
    2hi
    3(empty)
  33. System.out.println("\"hello world\": " + combined.validate("hello worl…

    138Validator<String> combined = Validator.and(notEmpty, minLengthRule);139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));
    output"hello world": ✓ Valid
  34. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  35. System.out.println("\"hi\": " + combined.validate("hi"));

    139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));
    output"hi": ✗ Invalid: Value cannot be empty AND Value must be at least 5 characters
  36. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  37. email ← ⟨EmailValidator E⟩

    140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));142143// Custom validator uses default method //?usecustom144System.out.println("\n--- Custom EmailValidator ---");145Validator<String> email→ ⟨EmailValidator E⟩ = new EmailValidator();146System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147System.out.println("\"invalid-email\": " + email.validate("invalid-email"));
    output"": ✗ Invalid: Value cannot be empty AND Value must be at least 5 characters
    
    --- Custom EmailValidator ---
  38. @Override public boolean isValid(String value)

    pass 1 of 2
    100class EmailValidator implements Validator<String> {101    @Override102    public boolean isValid(String valuetest@example.com) {103        return valuetest@example.com != null && value.contains("@") && value.contains(".");104    }
  39. System.out.println("\"test@example.com\": " + email.validate("test@exa…

    145Validator<String> email = new EmailValidator();146System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147System.out.println("\"invalid-email\": " + email.validate("invalid-email"));
    output"test@example.com": ✓ Valid
  40. @Override public boolean isValid(String value)

    pass 2 of 2
    100class EmailValidator implements Validator<String> {101    @Override102    public boolean isValid(String valueinvalid-email) {103        return valueinvalid-email != null && value.contains("@") && value.contains(".");104    }
  41. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  42. System.out.println("\"invalid-email\": " + email.validate("invalid-ema…

    146    System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147    System.out.println("\"invalid-email\": " + email.validate("invalid-email"));148    149    System.out.println("\n=== Design Summary ===");150    System.out.println("""151        Static methods:152        - Validator.notEmpty() - factory for common validator153        - Validator.minLength(n) - configurable factory154        - Validator.range(min, max) - another factory155        - Validator.and(v1, v2) - combinator156        157        Default method:158        - validate(value) - uses isValid() and getErrorMessage()159        160        Abstract methods:161        - isValid() - must implement162        - getErrorMessage() - must implement163        164        This pattern:165        - Factories create pre-built validators166        - Custom validators implement interface167        - All get validate() for free168        """);169}
    output"invalid-email": ✗ Invalid: Invalid email format
    
    === Design Summary ===
    Static methods:
    - Validator.notEmpty() - factory for common validator
    - Validator.minLength(n) - configurable factory
    - Validator.range(min, max) - another factory
    - Validator.and(v1, v2) - combinator
    
    Default method:
    - validate(value) - uses isValid() and getErrorMessage()
    
    Abstract methods:
    - isValid() - must implement
    - getErrorMessage() - must implement
    
    This pattern:
    - Factories create pre-built validators
    - Custom validators implement interface
    - All get validate() for free
  1. public static void main(String[] args)

    112public class StaticWithDefault {113    public static void main(String[] args) {114        System.out.println("=== Validator Interface ===\n");115        116        // Using static factory methods117        Validator<String> notEmpty = Validator.notEmpty();118        int minChars = 3;
    output=== Validator Interface ===
  2. notEmpty ← ⟨Validator$1 A⟩, minChars ← 3

    116// Using static factory methods117Validator<String> notEmpty→ ⟨Validator$1 A⟩ = Validator.notEmpty();118int minChars→ 3 = 3;119Validator<String> minLengthRule = Validator.minLength(minChars3);120Validator<Integer> ageRange = Validator.range(18, 65);
  3. static Validator<String> minLength(int min)

    31static Validator<String> minLength(int min3) {32    return new Validator<>() {33        @Override34        public boolean isValid(String value) {35            return value != null && value.length() >= min;36        }37        38        @Override39        public String getErrorMessage() {40            return "Value must be at least " + min + " characters";41        }42    };43}
  4. minLengthRule ← ⟨Validator$2 B⟩

    118int minChars = 3;119Validator<String> minLengthRule→ ⟨Validator$2 B⟩ = Validator.minLength(minChars3);120Validator<Integer> ageRange = Validator.range(18, 65);
  5. static Validator<Integer> range(int min, int max)

    45static Validator<Integer> range(int min18, int max65) {46    return new Validator<>() {47        @Override48        public boolean isValid(Integer value) {49            return value != null && value >= min && value <= max;50        }51        52        @Override53        public String getErrorMessage() {54            return "Value must be between " + min + " and " + max;55        }56    };57}
  6. ageRange ← ⟨Validator$3 C⟩

    119Validator<String> minLengthRule = Validator.minLength(minChars);120Validator<Integer> ageRange→ ⟨Validator$3 C⟩ = Validator.range(18, 65);121122System.out.println("--- notEmpty validator ---");123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));
    output--- notEmpty validator ---
  7. default ValidationResult validate(T value)

    pass 1 of 13
    8// Default - common behavior9default ValidationResult validate(T valuehello) {10    if (isValid(value)) {
    13 passes — pass 1 is the card above
    passvalue
    1hello
    2(empty)
    3null
    4hello
    5hi
    625
    715
    870
    9hello world
    ⋯ 2 more passes ⋯
    12test@example.com
    13invalid-email
  8. @Override public boolean isValid(String value)

    pass 1 of 6
    18return new Validator<>() {19    @Override20    public boolean isValid(String valuehello) {21        return valuehello != null && !value.isEmpty();22    }
    All 6 passes — pass 1 is the card above
    passvalue
    1hello
    2(empty)
    3null
    4hello world
    5hi
    6(empty)
  9. if (isValid(value))

    pass 1 of 5
    9default ValidationResult validate(T value) {10    if (isValid(valuehello)) {11        return ValidationResult.success();12    }
    All 5 passes — pass 1 is the card above
    passvalue
    1hello
    2hello
    325
    4hello world
    5test@example.com
  10. this.valid ← true, this.message ← OK

    pass 1 of 13
    80private ValidationResult(boolean validtrue, String messageOK) {81    this.valid→ true = validtrue;82    this.message→ OK = messageOK;83}
    13 passes — pass 1 is the card above
    passvalidmessagethis.validthis.message
    1trueOKtrueOK
    2falseValue cannot be emptyfalseValue cannot be empty
    3falseValue cannot be emptyfalseValue cannot be empty
    4trueOKtrueOK
    5falseValue must be at least 3 charactersfalseValue must be at least 3 characters
    6trueOKtrueOK
    7falseValue must be between 18 and 65falseValue must be between 18 and 65
    8falseValue must be between 18 and 65falseValue must be between 18 and 65
    9trueOKtrueOK
    ⋯ 2 more passes ⋯
    12trueOKtrueOK
    13falseInvalid email formatfalseInvalid email format
  11. System.out.println("\"hello\": " + notEmpty.validate("hello"));

    122System.out.println("--- notEmpty validator ---");123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));
    output"hello": ✓ Valid
  12. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  13. static ValidationResult failure(String message)

    pass 1 of 8
    89static ValidationResult failure(String messageValue cannot be empty) {90    return new ValidationResult(false, message);91}
    All 8 passes — pass 1 is the card above
    passmessage
    1Value cannot be empty
    2Value cannot be empty
    3Value must be at least 3 characters
    4Value must be between 18 and 65
    5Value must be between 18 and 65
    6Value cannot be empty AND Value must be at least 3 characters
    7Value cannot be empty AND Value must be at least 3 characters
    8Invalid email format
  14. System.out.println("\"\": " + notEmpty.validate(""));

    123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));
    output"": ✗ Invalid: Value cannot be empty
  15. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  16. System.out.println(" --- minLength(" + minChars + ") validator ---");

    124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));126127System.out.println("\n--- minLength(" + minChars3 + ") validator ---");128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));
    outputnull: ✗ Invalid: Value cannot be empty
    
    --- minLength(3) validator ---
  17. @Override public boolean isValid(String value)

    pass 1 of 4
    32return new Validator<>() {33    @Override34    public boolean isValid(String valuehello) {35        return valuehello != null && value.length() >= min3;36    }
    All 4 passes — pass 1 is the card above
    passvalue
    1hello
    2hi
    3hello world
    4hi
  18. System.out.println("\"hello\": " + minLengthRule.validate("hello"));

    127System.out.println("\n--- minLength(" + minChars + ") validator ---");128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));
    output"hello": ✓ Valid
  19. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  20. @Override public String getErrorMessage()

    pass 1 of 3
    38@Override39public String getErrorMessage() {40    return "Value must be at least " + min3 + " characters";41}
  21. System.out.println("\"hi\": " + minLengthRule.validate("hi"));

    128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));130131System.out.println("\n--- range(18, 65) validator ---");132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));
    output"hi": ✗ Invalid: Value must be at least 3 characters
    
    --- range(18, 65) validator ---
  22. @Override public boolean isValid(Integer value)

    pass 1 of 3
    46return new Validator<>() {47    @Override48    public boolean isValid(Integer value25) {49        return value25 != null && value >= min18 && value <= max65;50    }
    All 3 passes — pass 1 is the card above
    passvalue
    125
    215
    370
  23. System.out.println("25: " + ageRange.validate(25));

    131System.out.println("\n--- range(18, 65) validator ---");132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));
    output25: ✓ Valid
  24. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  25. @Override public String getErrorMessage()

    pass 1 of 2
    52@Override53public String getErrorMessage() {54    return "Value must be between " + min18 + " and " + max65;55}
  26. System.out.println("15: " + ageRange.validate(15));

    132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));
    output15: ✗ Invalid: Value must be between 18 and 65
  27. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  28. @Override public String getErrorMessage()

    pass 2 of 2
    52@Override53public String getErrorMessage() {54    return "Value must be between " + min18 + " and " + max65;55}
  29. Validator<String> combined = Validator.and(notEmpty, minLengthRule);

    133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));135136// Using combinator137System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");138Validator<String> combined = Validator.and(notEmpty⟨Validator$1 A⟩, minLengthRule⟨Validator$2 B⟩);139System.out.println("\"hello world\": " + combined.validate("hello world"));
    output70: ✗ Invalid: Value must be between 18 and 65
    
    --- Combined validator (notEmpty AND minLength) ---
  30. static <T> Validator<T> and(Validator<T> v1, Validator<T> v2)

    59// Static combinator methods60static <T> Validator<T> and(Validator<T> v1⟨Validator$1 A⟩, Validator<T> v2⟨Validator$2 B⟩) {61    return new Validator<>() {62        @Override63        public boolean isValid(T value) {64            return v1.isValid(value) && v2.isValid(value);65        }66        67        @Override68        public String getErrorMessage() {69            return v1.getErrorMessage() + " AND " + v2.getErrorMessage();70        }71    };72}
  31. combined ← ⟨Validator$4 D⟩

    137System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");138Validator<String> combined→ ⟨Validator$4 D⟩ = Validator.and(notEmpty⟨Validator$1 A⟩, minLengthRule⟨Validator$2 B⟩);139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));
  32. @Override public boolean isValid(T value)

    pass 1 of 3
    61return new Validator<>() {62    @Override63    public boolean isValid(T valuehello world) {64        return v1.isValid(valuehello world) && v2.isValid(value);65    }
    All 3 passes — pass 1 is the card above
    passvalue
    1hello world
    2hi
    3(empty)
  33. System.out.println("\"hello world\": " + combined.validate("hello worl…

    138Validator<String> combined = Validator.and(notEmpty, minLengthRule);139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));
    output"hello world": ✓ Valid
  34. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  35. System.out.println("\"hi\": " + combined.validate("hi"));

    139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));
    output"hi": ✗ Invalid: Value cannot be empty AND Value must be at least 3 characters
  36. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  37. email ← ⟨EmailValidator E⟩

    140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));142143// Custom validator uses default method144System.out.println("\n--- Custom EmailValidator ---");145Validator<String> email→ ⟨EmailValidator E⟩ = new EmailValidator();146System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147System.out.println("\"invalid-email\": " + email.validate("invalid-email"));
    output"": ✗ Invalid: Value cannot be empty AND Value must be at least 3 characters
    
    --- Custom EmailValidator ---
  38. @Override public boolean isValid(String value)

    pass 1 of 2
    100class EmailValidator implements Validator<String> {101    @Override102    public boolean isValid(String valuetest@example.com) {103        return valuetest@example.com != null && value.contains("@") && value.contains(".");104    }
  39. System.out.println("\"test@example.com\": " + email.validate("test@exa…

    145Validator<String> email = new EmailValidator();146System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147System.out.println("\"invalid-email\": " + email.validate("invalid-email"));
    output"test@example.com": ✓ Valid
  40. @Override public boolean isValid(String value)

    pass 2 of 2
    100class EmailValidator implements Validator<String> {101    @Override102    public boolean isValid(String valueinvalid-email) {103        return valueinvalid-email != null && value.contains("@") && value.contains(".");104    }
  41. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  42. System.out.println("\"invalid-email\": " + email.validate("invalid-ema…

    146    System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147    System.out.println("\"invalid-email\": " + email.validate("invalid-email"));148    149    System.out.println("\n=== Design Summary ===");150    System.out.println("""151        Static methods:152        - Validator.notEmpty() - factory for common validator153        - Validator.minLength(n) - configurable factory154        - Validator.range(min, max) - another factory155        - Validator.and(v1, v2) - combinator156        157        Default method:158        - validate(value) - uses isValid() and getErrorMessage()159        160        Abstract methods:161        - isValid() - must implement162        - getErrorMessage() - must implement163        164        This pattern:165        - Factories create pre-built validators166        - Custom validators implement interface167        - All get validate() for free168        """);169}
    output"invalid-email": ✗ Invalid: Invalid email format
    
    === Design Summary ===
    Static methods:
    - Validator.notEmpty() - factory for common validator
    - Validator.minLength(n) - configurable factory
    - Validator.range(min, max) - another factory
    - Validator.and(v1, v2) - combinator
    
    Default method:
    - validate(value) - uses isValid() and getErrorMessage()
    
    Abstract methods:
    - isValid() - must implement
    - getErrorMessage() - must implement
    
    This pattern:
    - Factories create pre-built validators
    - Custom validators implement interface
    - All get validate() for free
  1. public static void main(String[] args)

    112public class StaticWithDefault {113    public static void main(String[] args) {114        System.out.println("=== Validator Interface ===\n");115        116        // Using static factory methods117        Validator<String> notEmpty = Validator.notEmpty();118        int minChars = 8;
    output=== Validator Interface ===
  2. notEmpty ← ⟨Validator$1 A⟩, minChars ← 8

    116// Using static factory methods117Validator<String> notEmpty→ ⟨Validator$1 A⟩ = Validator.notEmpty();118int minChars→ 8 = 8;119Validator<String> minLengthRule = Validator.minLength(minChars8);120Validator<Integer> ageRange = Validator.range(18, 65);
  3. static Validator<String> minLength(int min)

    31static Validator<String> minLength(int min8) {32    return new Validator<>() {33        @Override34        public boolean isValid(String value) {35            return value != null && value.length() >= min;36        }37        38        @Override39        public String getErrorMessage() {40            return "Value must be at least " + min + " characters";41        }42    };43}
  4. minLengthRule ← ⟨Validator$2 B⟩

    118int minChars = 8;119Validator<String> minLengthRule→ ⟨Validator$2 B⟩ = Validator.minLength(minChars8);120Validator<Integer> ageRange = Validator.range(18, 65);
  5. static Validator<Integer> range(int min, int max)

    45static Validator<Integer> range(int min18, int max65) {46    return new Validator<>() {47        @Override48        public boolean isValid(Integer value) {49            return value != null && value >= min && value <= max;50        }51        52        @Override53        public String getErrorMessage() {54            return "Value must be between " + min + " and " + max;55        }56    };57}
  6. ageRange ← ⟨Validator$3 C⟩

    119Validator<String> minLengthRule = Validator.minLength(minChars);120Validator<Integer> ageRange→ ⟨Validator$3 C⟩ = Validator.range(18, 65);121122System.out.println("--- notEmpty validator ---");123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));
    output--- notEmpty validator ---
  7. default ValidationResult validate(T value)

    pass 1 of 13
    8// Default - common behavior9default ValidationResult validate(T valuehello) {10    if (isValid(value)) {
    13 passes — pass 1 is the card above
    passvalue
    1hello
    2(empty)
    3null
    4hello
    5hi
    625
    715
    870
    9hello world
    ⋯ 2 more passes ⋯
    12test@example.com
    13invalid-email
  8. @Override public boolean isValid(String value)

    pass 1 of 6
    18return new Validator<>() {19    @Override20    public boolean isValid(String valuehello) {21        return valuehello != null && !value.isEmpty();22    }
    All 6 passes — pass 1 is the card above
    passvalue
    1hello
    2(empty)
    3null
    4hello world
    5hi
    6(empty)
  9. if (isValid(value))

    pass 1 of 4
    9default ValidationResult validate(T value) {10    if (isValid(valuehello)) {11        return ValidationResult.success();12    }
    All 4 passes — pass 1 is the card above
    passvalue
    1hello
    225
    3hello world
    4test@example.com
  10. this.valid ← true, this.message ← OK

    pass 1 of 13
    80private ValidationResult(boolean validtrue, String messageOK) {81    this.valid→ true = validtrue;82    this.message→ OK = messageOK;83}
    13 passes — pass 1 is the card above
    passvalidmessagethis.validthis.message
    1trueOKtrueOK
    2falseValue cannot be emptyfalseValue cannot be empty
    3falseValue cannot be emptyfalseValue cannot be empty
    4falseValue must be at least 8 charactersfalseValue must be at least 8 characters
    5falseValue must be at least 8 charactersfalseValue must be at least 8 characters
    6trueOKtrueOK
    7falseValue must be between 18 and 65falseValue must be between 18 and 65
    8falseValue must be between 18 and 65falseValue must be between 18 and 65
    9trueOKtrueOK
    ⋯ 2 more passes ⋯
    12trueOKtrueOK
    13falseInvalid email formatfalseInvalid email format
  11. System.out.println("\"hello\": " + notEmpty.validate("hello"));

    122System.out.println("--- notEmpty validator ---");123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));
    output"hello": ✓ Valid
  12. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  13. static ValidationResult failure(String message)

    pass 1 of 9
    89static ValidationResult failure(String messageValue cannot be empty) {90    return new ValidationResult(false, message);91}
    All 9 passes — pass 1 is the card above
    passmessage
    1Value cannot be empty
    2Value cannot be empty
    3Value must be at least 8 characters
    4Value must be at least 8 characters
    5Value must be between 18 and 65
    6Value must be between 18 and 65
    7Value cannot be empty AND Value must be at least 8 characters
    8Value cannot be empty AND Value must be at least 8 characters
    9Invalid email format
  14. System.out.println("\"\": " + notEmpty.validate(""));

    123System.out.println("\"hello\": " + notEmpty.validate("hello"));124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));
    output"": ✗ Invalid: Value cannot be empty
  15. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  16. System.out.println(" --- minLength(" + minChars + ") validator ---");

    124System.out.println("\"\": " + notEmpty.validate(""));125System.out.println("null: " + notEmpty.validate(null));126127System.out.println("\n--- minLength(" + minChars8 + ") validator ---");128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));
    outputnull: ✗ Invalid: Value cannot be empty
    
    --- minLength(8) validator ---
  17. @Override public boolean isValid(String value)

    pass 1 of 4
    32return new Validator<>() {33    @Override34    public boolean isValid(String valuehello) {35        return valuehello != null && value.length() >= min8;36    }
    All 4 passes — pass 1 is the card above
    passvalue
    1hello
    2hi
    3hello world
    4hi
  18. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  19. @Override public String getErrorMessage()

    pass 1 of 4
    38@Override39public String getErrorMessage() {40    return "Value must be at least " + min8 + " characters";41}
  20. System.out.println("\"hello\": " + minLengthRule.validate("hello"));

    127System.out.println("\n--- minLength(" + minChars + ") validator ---");128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));
    output"hello": ✗ Invalid: Value must be at least 8 characters
  21. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  22. System.out.println("\"hi\": " + minLengthRule.validate("hi"));

    128System.out.println("\"hello\": " + minLengthRule.validate("hello"));129System.out.println("\"hi\": " + minLengthRule.validate("hi"));130131System.out.println("\n--- range(18, 65) validator ---");132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));
    output"hi": ✗ Invalid: Value must be at least 8 characters
    
    --- range(18, 65) validator ---
  23. @Override public boolean isValid(Integer value)

    pass 1 of 3
    46return new Validator<>() {47    @Override48    public boolean isValid(Integer value25) {49        return value25 != null && value >= min18 && value <= max65;50    }
    All 3 passes — pass 1 is the card above
    passvalue
    125
    215
    370
  24. System.out.println("25: " + ageRange.validate(25));

    131System.out.println("\n--- range(18, 65) validator ---");132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));
    output25: ✓ Valid
  25. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  26. @Override public String getErrorMessage()

    pass 1 of 2
    52@Override53public String getErrorMessage() {54    return "Value must be between " + min18 + " and " + max65;55}
  27. System.out.println("15: " + ageRange.validate(15));

    132System.out.println("25: " + ageRange.validate(25));133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));
    output15: ✗ Invalid: Value must be between 18 and 65
  28. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  29. @Override public String getErrorMessage()

    pass 2 of 2
    52@Override53public String getErrorMessage() {54    return "Value must be between " + min18 + " and " + max65;55}
  30. Validator<String> combined = Validator.and(notEmpty, minLengthRule);

    133System.out.println("15: " + ageRange.validate(15));134System.out.println("70: " + ageRange.validate(70));135136// Using combinator137System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");138Validator<String> combined = Validator.and(notEmpty⟨Validator$1 A⟩, minLengthRule⟨Validator$2 B⟩);139System.out.println("\"hello world\": " + combined.validate("hello world"));
    output70: ✗ Invalid: Value must be between 18 and 65
    
    --- Combined validator (notEmpty AND minLength) ---
  31. static <T> Validator<T> and(Validator<T> v1, Validator<T> v2)

    59// Static combinator methods60static <T> Validator<T> and(Validator<T> v1⟨Validator$1 A⟩, Validator<T> v2⟨Validator$2 B⟩) {61    return new Validator<>() {62        @Override63        public boolean isValid(T value) {64            return v1.isValid(value) && v2.isValid(value);65        }66        67        @Override68        public String getErrorMessage() {69            return v1.getErrorMessage() + " AND " + v2.getErrorMessage();70        }71    };72}
  32. combined ← ⟨Validator$4 D⟩

    137System.out.println("\n--- Combined validator (notEmpty AND minLength) ---");138Validator<String> combined→ ⟨Validator$4 D⟩ = Validator.and(notEmpty⟨Validator$1 A⟩, minLengthRule⟨Validator$2 B⟩);139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));
  33. @Override public boolean isValid(T value)

    pass 1 of 3
    61return new Validator<>() {62    @Override63    public boolean isValid(T valuehello world) {64        return v1.isValid(valuehello world) && v2.isValid(value);65    }
    All 3 passes — pass 1 is the card above
    passvalue
    1hello world
    2hi
    3(empty)
  34. System.out.println("\"hello world\": " + combined.validate("hello worl…

    138Validator<String> combined = Validator.and(notEmpty, minLengthRule);139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));
    output"hello world": ✓ Valid
  35. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  36. System.out.println("\"hi\": " + combined.validate("hi"));

    139System.out.println("\"hello world\": " + combined.validate("hello world"));140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));
    output"hi": ✗ Invalid: Value cannot be empty AND Value must be at least 8 characters
  37. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  38. email ← ⟨EmailValidator E⟩

    140System.out.println("\"hi\": " + combined.validate("hi"));141System.out.println("\"\": " + combined.validate(""));142143// Custom validator uses default method144System.out.println("\n--- Custom EmailValidator ---");145Validator<String> email→ ⟨EmailValidator E⟩ = new EmailValidator();146System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147System.out.println("\"invalid-email\": " + email.validate("invalid-email"));
    output"": ✗ Invalid: Value cannot be empty AND Value must be at least 8 characters
    
    --- Custom EmailValidator ---
  39. @Override public boolean isValid(String value)

    pass 1 of 2
    100class EmailValidator implements Validator<String> {101    @Override102    public boolean isValid(String valuetest@example.com) {103        return valuetest@example.com != null && value.contains("@") && value.contains(".");104    }
  40. System.out.println("\"test@example.com\": " + email.validate("test@exa…

    145Validator<String> email = new EmailValidator();146System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147System.out.println("\"invalid-email\": " + email.validate("invalid-email"));
    output"test@example.com": ✓ Valid
  41. @Override public boolean isValid(String value)

    pass 2 of 2
    100class EmailValidator implements Validator<String> {101    @Override102    public boolean isValid(String valueinvalid-email) {103        return valueinvalid-email != null && value.contains("@") && value.contains(".");104    }
  42. return ValidationResult.failure(getErrorMessage());

    12    }13    return ValidationResult.failure(getErrorMessage());14}
  43. System.out.println("\"invalid-email\": " + email.validate("invalid-ema…

    146    System.out.println("\"test@example.com\": " + email.validate("test@example.com"));147    System.out.println("\"invalid-email\": " + email.validate("invalid-email"));148    149    System.out.println("\n=== Design Summary ===");150    System.out.println("""151        Static methods:152        - Validator.notEmpty() - factory for common validator153        - Validator.minLength(n) - configurable factory154        - Validator.range(min, max) - another factory155        - Validator.and(v1, v2) - combinator156        157        Default method:158        - validate(value) - uses isValid() and getErrorMessage()159        160        Abstract methods:161        - isValid() - must implement162        - getErrorMessage() - must implement163        164        This pattern:165        - Factories create pre-built validators166        - Custom validators implement interface167        - All get validate() for free168        """);169}
    output"invalid-email": ✗ Invalid: Invalid email format
    
    === Design Summary ===
    Static methods:
    - Validator.notEmpty() - factory for common validator
    - Validator.minLength(n) - configurable factory
    - Validator.range(min, max) - another factory
    - Validator.and(v1, v2) - combinator
    
    Default method:
    - validate(value) - uses isValid() and getErrorMessage()
    
    Abstract methods:
    - isValid() - must implement
    - getErrorMessage() - must implement
    
    This pattern:
    - Factories create pre-built validators
    - Custom validators implement interface
    - All get validate() for free

Static for utilities, default for shared implementation, abstract for required behavior.

Not inherited

Static methods don't become part of implementing classes.

NotInherited.java
Replay: real traced execution (multi-file project)
// Static Methods Are NOT Inherited

interface Counter {
    // Static method
    static int defaultStart() {
        return 0;
    }

    // Default method
    default void increment() {
        System.out.println("Incrementing...");
    }

    // Abstract method
    int getCount();
}

class SimpleCounter implements Counter {
    private int count = Counter.defaultStart();

    @Override
    public int getCount() {
        return count;
    }

    // Note: we DON'T have defaultStart() method!
    // Static methods are NOT inherited
}

// Let's prove it
class InheritanceDemo {

    // This would work with default method
    static void testDefault(Counter counter) {
        counter.increment();  // Works! Default is inherited
    }

    // Cannot call static via instance
    static void showStaticDifference() {
        SimpleCounter sc = new SimpleCounter();

        // WORKS - default method via instance
        sc.increment();  // inherited!

        // DOES NOT WORK - static via instance
        // sc.defaultStart();  // COMPILE ERROR!

        // Must use interface name
        int start = Counter.defaultStart();
        System.out.println("Start value: " + start);
    }
}

// What if class has same-named static method?
interface Vehicle {
    static String getType() {
        return "Generic Vehicle";
    }
}

class Car implements Vehicle {
    // This is NOT overriding!
    static String getType() {
        return "Car";
    }

    // It's a completely separate method
    // Car.getType() and Vehicle.getType() are different
}

// Contrast with default methods
interface Animal {
    default String speak() {
        return "Some sound";
    }
}

class Dog implements Animal {
    @Override  // This IS overriding
    public String speak() {
        return "Woof!";
    }
}

public class NotInherited {
    public static void main(String[] args) {
        System.out.println("=== Static vs Default Inheritance ===\n");

        // Default method IS inherited
        System.out.println("--- Default Method (inherited) ---");
        Dog dog = new Dog();
        System.out.println("dog.speak() = " + dog.speak());  // Overridden

        Animal genericAnimal = new Animal() {
            // Uses default
        };
        System.out.println("genericAnimal.speak() = " + genericAnimal.speak());  // Default

        // Static method NOT inherited
        System.out.println("\n--- Static Method (NOT inherited) ---");
        System.out.println("Vehicle.getType() = " + Vehicle.getType());
        System.out.println("Car.getType() = " + Car.getType());  // Different method!

        // They are completely separate
        System.out.println("\n--- Proving Separation ---");
        Vehicle v = new Car();
        // v.getType();  // COMPILE ERROR - no instance method
        System.out.println("Vehicle.getType() on Car instance: " + Vehicle.getType());
        System.out.println("Car.getType() directly: " + Car.getType());

        System.out.println("\n--- Counter Example ---");
        SimpleCounter counter = new SimpleCounter();
        counter.increment();  // Default - inherited
        // counter.defaultStart();  // Static - NOT available!
        System.out.println("Counter.defaultStart() = " + Counter.defaultStart());
        System.out.println("counter.getCount() = " + counter.getCount());

        System.out.println("\n=== Summary ===");
        System.out.println("""
            +-------------------+------------+--------------+
            | Feature           | Default    | Static       |
            +-------------------+------------+--------------+
            | Belongs to        | Instance   | Interface    |
            | Inherited         | YES        | NO           |
            | Can override      | YES        | NO           |
            | Call via instance | YES        | NO           |
            | Call via interface| NO         | YES          |
            +-------------------+------------+--------------+

            Key insight:
            - Default methods behave like instance methods
            - Static methods belong ONLY to the interface
            - Same-named static in class is completely separate
            """);
    }
}
  1. dog ← ⟨Dog A⟩

    85public class NotInherited {86    public static void main(String[] args) {87        System.out.println("=== Static vs Default Inheritance ===\n");88        89        // Default method IS inherited //?testdefault90        System.out.println("--- Default Method (inherited) ---");91        Dog dog→ ⟨Dog A⟩ = new Dog();92        System.out.println("dog.speak() = " + dog.speak());  // Overridden
    output=== Static vs Default Inheritance ===
    --- Default Method (inherited) ---
  2. genericAnimal ← ⟨NotInherited$1 B⟩

    91Dog dog = new Dog();92System.out.println("dog.speak() = " + dog.speak());  // Overridden9394Animal genericAnimal→ ⟨NotInherited$1 B⟩ = new Animal() {95    // Uses default96};97System.out.println("genericAnimal.speak() = " + genericAnimal.speak());  // Default
    outputdog.speak() = Woof!
  3. System.out.println("genericAnimal.speak() = " + genericAnimal.speak())…

    96};97System.out.println("genericAnimal.speak() = " + genericAnimal.speak());  // Default9899// Static method NOT inherited //?teststatic100System.out.println("\n--- Static Method (NOT inherited) ---");101System.out.println("Vehicle.getType() = " + Vehicle.getType());102System.out.println("Car.getType() = " + Car.getType());  // Different method!
    outputgenericAnimal.speak() = Some sound
    
    --- Static Method (NOT inherited) ---
  4. System.out.println("Vehicle.getType() = " + Vehicle.getType());

    100System.out.println("\n--- Static Method (NOT inherited) ---");101System.out.println("Vehicle.getType() = " + Vehicle.getType());102System.out.println("Car.getType() = " + Car.getType());  // Different method!
    outputVehicle.getType() = Generic Vehicle
  5. v ← ⟨Car C⟩

    101System.out.println("Vehicle.getType() = " + Vehicle.getType());102System.out.println("Car.getType() = " + Car.getType());  // Different method!103104// They are completely separate105System.out.println("\n--- Proving Separation ---");106Vehicle v→ ⟨Car C⟩ = new Car();107// v.getType();  // COMPILE ERROR - no instance method108System.out.println("Vehicle.getType() on Car instance: " + Vehicle.getType());109System.out.println("Car.getType() directly: " + Car.getType());
    outputCar.getType() = Car
    
    --- Proving Separation ---
  6. System.out.println("Vehicle.getType() on Car instance: " + Vehicle.get…

    107// v.getType();  // COMPILE ERROR - no instance method108System.out.println("Vehicle.getType() on Car instance: " + Vehicle.getType());109System.out.println("Car.getType() directly: " + Car.getType());
    outputVehicle.getType() on Car instance: Generic Vehicle
  7. System.out.println("Car.getType() directly: " + Car.getType());

    108System.out.println("Vehicle.getType() on Car instance: " + Vehicle.getType());109System.out.println("Car.getType() directly: " + Car.getType());110111System.out.println("\n--- Counter Example ---");112SimpleCounter counter = new SimpleCounter();113counter.increment();  // Default - inherited
    outputCar.getType() directly: Car
    
    --- Counter Example ---
  8. counter ← ⟨SimpleCounter D⟩

    111System.out.println("\n--- Counter Example ---");112SimpleCounter counter→ ⟨SimpleCounter D⟩ = new SimpleCounter();113counter.increment();  // Default - inherited114// counter.defaultStart();  // Static - NOT available!
  9. default void increment()

    9// Default method //?defaultmethod10default void increment() {11    System.out.println("Incrementing...");12}
    outputIncrementing...
  10. counter.increment(); // Default - inherited

    112SimpleCounter counter = new SimpleCounter();113counter.increment();  // Default - inherited114// counter.defaultStart();  // Static - NOT available!115System.out.println("Counter.defaultStart() = " + Counter.defaultStart());116System.out.println("counter.getCount() = " + counter.getCount());
  11. System.out.println("Counter.defaultStart() = " + Counter.defaultStart(…

    114// counter.defaultStart();  // Static - NOT available!115System.out.println("Counter.defaultStart() = " + Counter.defaultStart());116System.out.println("counter.getCount() = " + counter.getCount());
    outputCounter.defaultStart() = 0
  12. @Override public int getCount()

    21@Override22public int getCount() {23    return count0;24}
  13. System.out.println("counter.getCount() = " + counter.getCount());

    115    System.out.println("Counter.defaultStart() = " + Counter.defaultStart());116    System.out.println("counter.getCount() = " + counter.getCount());117    118    System.out.println("\n=== Summary ===");119    System.out.println("""120        +-------------------+------------+--------------+121        | Feature           | Default    | Static       |122        +-------------------+------------+--------------+123        | Belongs to        | Instance   | Interface    |124        | Inherited         | YES        | NO           |125        | Can override      | YES        | NO           |126        | Call via instance | YES        | NO           |127        | Call via interface| NO         | YES          |128        +-------------------+------------+--------------+129        130        Key insight:131        - Default methods behave like instance methods132        - Static methods belong ONLY to the interface133        - Same-named static in class is completely separate134        """);135}
    outputcounter.getCount() = 0
    
    === Summary ===
    +-------------------+------------+--------------+
    | Feature           | Default    | Static       |
    +-------------------+------------+--------------+
    | Belongs to        | Instance   | Interface    |
    | Inherited         | YES        | NO           |
    | Can override      | YES        | NO           |
    | Call via instance | YES        | NO           |
    | Call via interface| NO         | YES          |
    +-------------------+------------+--------------+
    
    Key insight:
    - Default methods behave like instance methods
    - Static methods belong ONLY to the interface
    - Same-named static in class is completely separate

Call via interface name only. Implementing class doesn't get the method.

Exercise: Practical.java

Build a complete interface with static factories and utilities