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
// 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
            """);
    }
}

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.

FactoryMethods.java
// 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 = ;
        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 = ;
        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 = ;
        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)
            """);
    }
}

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

Utility methods

Helper functions related to the interface's purpose.

UtilityMethods.java
// 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
            """);
    }
}

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

Static with default

Combine static and default methods in one interface.

StaticWithDefault.java
// 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 = ;
        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 = ;
        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 = ;
        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
            """);
    }
}

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

Not inherited

Static methods don't become part of implementing classes.

NotInherited.java
// 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
            """);
    }
}

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

Exercise: Practical.java

Build a complete interface with static factories and utilities