javaintermediate

Records and Sealed Types — ADT Modeling

Model algebraic data types with records and sealed interfaces for type-safe domain modeling.

java
import java.util.List;

// Sealed hierarchy — Result type (like Rust's Result)
public sealed interface Result<T> {
    record Ok<T>(T value) implements Result<T> {}
    record Err<T>(String message, Exception cause) implements Result<T> {
        Err(String message) { this(message, null); }
    }

    default <U> Result<U> map(java.util.function.Function<T, U> fn) {
        return switch (this) {
            case Ok<T> ok -> new Ok<>(fn.apply(ok.value()));
            case Err<T> err -> new Err<>(err.message(), err.cause());
        };
    }

    default <U> Result<U> flatMap(java.util.function.Function<T, Result<U>> fn) {
        return switch (this) {
            case Ok<T> ok -> fn.apply(ok.value());
            case Err<T> err -> new Err<>(err.message(), err.cause());
        };
    }

    default T orElse(T fallback) {
        return switch (this) {
            case Ok<T> ok -> ok.value();
            case Err<T> err -> fallback;
        };
    }

    static <T> Result<T> of(java.util.concurrent.Callable<T> action) {
        try { return new Ok<>(action.call()); }
        catch (Exception e) { return new Err<>(e.getMessage(), e); }
    }
}

// Domain events as sealed types
sealed interface DomainEvent {
    record UserRegistered(String userId, String email, java.time.Instant at) implements DomainEvent {}
    record OrderPlaced(String orderId, List<String> items, double total) implements DomainEvent {}
    record PaymentReceived(String orderId, String txId, double amount) implements DomainEvent {}
    record OrderShipped(String orderId, String trackingNo) implements DomainEvent {}
}

// Expression tree
sealed interface Expr {
    record Num(double value) implements Expr {}
    record Add(Expr left, Expr right) implements Expr {}
    record Mul(Expr left, Expr right) implements Expr {}
    record Neg(Expr expr) implements Expr {}

    static double eval(Expr expr) {
        return switch (expr) {
            case Num n   -> n.value();
            case Add a   -> eval(a.left()) + eval(a.right());
            case Mul m   -> eval(m.left()) * eval(m.right());
            case Neg neg -> -eval(neg.expr());
        };
    }
}

class Demo {
    public static void main(String[] args) {
        // Result usage
        Result<Integer> parsed = Result.of(() -> Integer.parseInt("42"));
        Result<String> formatted = parsed.map(n -> "Number: " + n);
        System.out.println(formatted); // Ok[value=Number: 42]

        Result<Integer> failed = Result.of(() -> Integer.parseInt("abc"));
        System.out.println(failed.orElse(0)); // 0

        // Expression tree
        Expr expr = new Expr.Add(
            new Expr.Num(3),
            new Expr.Mul(new Expr.Num(4), new Expr.Num(5))
        );
        System.out.println(Expr.eval(expr)); // 23.0
    }
}

Use Cases

  • Type-safe error handling with Result type
  • Domain event modeling in event-driven systems
  • Expression trees and interpreter patterns

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.