Explore Java's powerful deconstruction patterns including Records, Pattern Matching, and advanced destructuring techniques.
Table of Contents
- Record Deconstruction
- Pattern Matching with instanceof
- Switch Expression Patterns
- Custom Deconstruction Patterns
- Advanced Nesting Patterns
- Real-World Use Cases
Record Deconstruction
1. Basic Record Deconstruction
// Basic record definition
public record Person(String name, int age, String email) {
// Compact constructor with validation
public Person {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
if (email != null && !email.contains("@")) {
throw new IllegalArgumentException("Invalid email format");
}
}
// Static factory method
public static Person of(String name, int age) {
return new Person(name, age, null);
}
}
// Deconstruction examples
public class RecordDeconstruction {
public static void main(String[] args) {
// Create record instance
Person person = new Person("Alice", 30, "[email protected]");
// Traditional accessors
String name = person.name();
int age = person.age();
String email = person.email();
// Deconstruction in Java 16+ (conceptual - not actual syntax)
// This demonstrates the concept that will be available in future versions
System.out.println("Name: " + name + ", Age: " + age + ", Email: " + email);
}
// Method that demonstrates deconstruction concept
public static void processPerson(Person person) {
// Imagine future Java version supporting this:
// Person(String name, int age, String email) = person;
// Current approach - using accessors
String name = person.name();
int age = person.age();
String email = person.email();
System.out.printf("Processing %s (%d years old) - %s%n", name, age, email);
}
}
2. Advanced Record Patterns
// Complex record with nested records
public record Address(String street, String city, String zipCode, String country) {
public Address {
if (street == null || street.isBlank()) {
throw new IllegalArgumentException("Street cannot be empty");
}
}
public String format() {
return String.format("%s, %s %s, %s", street, city, zipCode, country);
}
}
public record Employee(
String id,
Person person,
Address address,
String department,
double salary
) {
public Employee {
if (salary < 0) {
throw new IllegalArgumentException("Salary cannot be negative");
}
}
public boolean isRemote() {
return address.city().equals("REMOTE");
}
}
// Record pattern matching examples
public class AdvancedRecordPatterns {
// Traditional approach to extracting data
public static void printEmployeeInfoTraditional(Employee employee) {
Person person = employee.person();
Address address = employee.address();
String name = person.name();
String city = address.city();
System.out.printf("Employee %s works in %s%n", name, city);
}
// Using method to simulate pattern matching
public static void extractEmployeeData(Employee employee) {
// Simulating what pattern matching might look like
String id = employee.id();
Person person = employee.person();
Address address = employee.address();
String department = employee.department();
double salary = employee.salary();
// Process extracted data
if (salary > 100000) {
System.out.printf("High earner: %s in %s%n",
person.name(), department);
}
}
// Process multiple records
public static void processEmployees(List<Employee> employees) {
for (Employee emp : employees) {
// Extract nested data
String empName = emp.person().name();
String empCity = emp.address().city();
double empSalary = emp.salary();
System.out.printf("%s in %s earns $%.2f%n",
empName, empCity, empSalary);
}
}
}
Pattern Matching with instanceof
3. Type Pattern Matching
// Hierarchy of shapes
public sealed interface Shape
permits Circle, Rectangle, Triangle, CompositeShape {
double area();
double perimeter();
}
public record Circle(double radius) implements Shape {
public Circle {
if (radius <= 0) {
throw new IllegalArgumentException("Radius must be positive");
}
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
}
public record Rectangle(double width, double height) implements Shape {
public Rectangle {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Dimensions must be positive");
}
}
@Override
public double area() {
return width * height;
}
@Override
public double perimeter() {
return 2 * (width + height);
}
public boolean isSquare() {
return width == height;
}
}
public record Triangle(double side1, double side2, double side3) implements Shape {
public Triangle {
if (side1 <= 0 || side2 <= 0 || side3 <= 0) {
throw new IllegalArgumentException("Sides must be positive");
}
// Triangle inequality validation
if (side1 + side2 <= side3 ||
side1 + side3 <= side2 ||
side2 + side3 <= side1) {
throw new IllegalArgumentException("Invalid triangle sides");
}
}
@Override
public double area() {
// Heron's formula
double s = perimeter() / 2;
return Math.sqrt(s * (s - side1) * (s - side2) * (s - side3));
}
@Override
public double perimeter() {
return side1 + side2 + side3;
}
}
public record CompositeShape(String name, List<Shape> components) implements Shape {
public CompositeShape {
if (components == null || components.isEmpty()) {
throw new IllegalArgumentException("Components cannot be empty");
}
}
@Override
public double area() {
return components.stream()
.mapToDouble(Shape::area)
.sum();
}
@Override
public double perimeter() {
// For composite shapes, perimeter might not be well-defined
return components.stream()
.mapToDouble(Shape::perimeter)
.sum();
}
}
// Pattern matching with instanceof
public class InstanceOfPatternMatching {
// Traditional instanceof with casting
public static String describeShapeTraditional(Shape shape) {
if (shape instanceof Circle) {
Circle circle = (Circle) shape;
return String.format("Circle with radius %.2f (area: %.2f)",
circle.radius(), circle.area());
} else if (shape instanceof Rectangle) {
Rectangle rect = (Rectangle) shape;
return String.format("Rectangle %.2f x %.2f (area: %.2f)",
rect.width(), rect.height(), rect.area());
} else if (shape instanceof Triangle) {
Triangle tri = (Triangle) shape;
return String.format("Triangle with sides %.2f, %.2f, %.2f",
tri.side1(), tri.side2(), tri.side3());
} else {
return "Unknown shape";
}
}
// Modern pattern matching with instanceof (Java 16+)
public static String describeShapeModern(Shape shape) {
if (shape instanceof Circle circle) {
return String.format("Circle with radius %.2f (area: %.2f)",
circle.radius(), circle.area());
} else if (shape instanceof Rectangle rect) {
String type = rect.isSquare() ? "Square" : "Rectangle";
return String.format("%s %.2f x %.2f (area: %.2f)",
type, rect.width(), rect.height(), rect.area());
} else if (shape instanceof Triangle tri) {
return String.format("Triangle with sides %.2f, %.2f, %.2f (perimeter: %.2f)",
tri.side1(), tri.side2(), tri.side3(), tri.perimeter());
} else if (shape instanceof CompositeShape comp) {
return String.format("Composite shape '%s' with %d components (total area: %.2f)",
comp.name(), comp.components().size(), comp.area());
} else {
return "Unknown shape";
}
}
// Pattern matching with additional conditions
public static String analyzeShape(Shape shape) {
if (shape instanceof Circle circle && circle.radius() > 10) {
return "Large circle: " + circle.radius();
} else if (shape instanceof Rectangle rect && rect.isSquare()) {
return "Square with side: " + rect.width();
} else if (shape instanceof Triangle tri) {
if (tri.side1() == tri.side2() && tri.side2() == tri.side3()) {
return "Equilateral triangle";
} else if (tri.side1() == tri.side2() ||
tri.side1() == tri.side3() ||
tri.side2() == tri.side3()) {
return "Isosceles triangle";
} else {
return "Scalene triangle";
}
} else {
return "Other shape";
}
}
// Processing collections with pattern matching
public static void processShapes(List<Shape> shapes) {
for (Shape shape : shapes) {
if (shape instanceof Circle circle) {
System.out.printf("Processing circle (r=%.2f)%n", circle.radius());
} else if (shape instanceof Rectangle rect) {
System.out.printf("Processing rectangle (%.2fx%.2f)%n",
rect.width(), rect.height());
}
// Additional processing...
}
}
}
Switch Expression Patterns
4. Enhanced Switch Patterns
// Domain models for different types of events
public sealed interface Event
permits UserEvent, SystemEvent, AuditEvent, ErrorEvent {
String id();
Instant timestamp();
String source();
}
public record UserEvent(
String id,
Instant timestamp,
String source,
String userId,
String action,
Map<String, Object> metadata
) implements Event {}
public record SystemEvent(
String id,
Instant timestamp,
String source,
String component,
String operation,
Duration duration
) implements Event {}
public record AuditEvent(
String id,
Instant timestamp,
String source,
String auditor,
String target,
String action,
boolean success
) implements Event {}
public record ErrorEvent(
String id,
Instant timestamp,
String source,
String errorType,
String message,
String stackTrace,
Severity severity
) implements Event {
public enum Severity {
LOW, MEDIUM, HIGH, CRITICAL
}
}
// Switch pattern matching examples
public class SwitchPatternMatching {
// Traditional switch statement
public static String processEventTraditional(Event event) {
if (event instanceof UserEvent) {
UserEvent userEvent = (UserEvent) event;
return "User action: " + userEvent.action();
} else if (event instanceof SystemEvent) {
SystemEvent sysEvent = (SystemEvent) event;
return "System operation: " + sysEvent.operation();
} else if (event instanceof AuditEvent) {
AuditEvent auditEvent = (AuditEvent) event;
return "Audit: " + auditEvent.action() + " - " +
(auditEvent.success() ? "SUCCESS" : "FAILED");
} else if (event instanceof ErrorEvent) {
ErrorEvent errorEvent = (ErrorEvent) event;
return "Error: " + errorEvent.message();
} else {
return "Unknown event";
}
}
// Modern switch expression with pattern matching (Java 21+ preview)
public static String processEventModern(Event event) {
return switch (event) {
case UserEvent userEvent ->
String.format("User %s performed: %s",
userEvent.userId(), userEvent.action());
case SystemEvent sysEvent when sysEvent.duration().toMinutes() > 5 ->
String.format("Long system operation: %s took %d minutes",
sysEvent.operation(), sysEvent.duration().toMinutes());
case SystemEvent sysEvent ->
String.format("System operation: %s completed", sysEvent.operation());
case AuditEvent auditEvent when !auditEvent.success() ->
String.format("AUDIT FAILURE: %s on %s",
auditEvent.action(), auditEvent.target());
case AuditEvent auditEvent ->
String.format("Audit: %s completed successfully", auditEvent.action());
case ErrorEvent errorEvent when errorEvent.severity() == ErrorEvent.Severity.CRITICAL ->
String.format("CRITICAL ERROR: %s - %s",
errorEvent.errorType(), errorEvent.message());
case ErrorEvent errorEvent ->
String.format("Error [%s]: %s",
errorEvent.severity(), errorEvent.message());
default -> "Unknown event type";
};
}
// Complex pattern matching with nested extraction
public static void analyzeEvents(List<Event> events) {
for (Event event : events) {
String analysis = switch (event) {
case UserEvent(var id, var timestamp, var source,
var userId, var action, var metadata) -> {
if (action.equals("LOGIN")) {
yield String.format("User %s logged in from %s", userId, source);
} else if (action.equals("LOGOUT")) {
yield String.format("User %s logged out", userId);
} else {
yield String.format("User %s performed %s", userId, action);
}
}
case SystemEvent(var id, var timestamp, var source,
var component, var operation, var duration)
when duration.toSeconds() > 60 -> {
yield String.format("Slow system operation: %s.%s took %d seconds",
component, operation, duration.toSeconds());
}
case ErrorEvent(var id, var timestamp, var source,
var errorType, var message, var stackTrace, var severity)
when severity == ErrorEvent.Severity.HIGH -> {
yield String.format("High severity error in %s: %s", source, message);
}
case null -> "Null event encountered";
default -> "Event type: " + event.getClass().getSimpleName();
};
System.out.println(analysis);
}
}
// Pattern matching with guards and complex conditions
public static String categorizeEvent(Event event) {
return switch (event) {
case UserEvent user when user.action().contains("DELETE") ->
"DESTRUCTIVE_USER_ACTION";
case UserEvent user when user.action().contains("CREATE") ->
"CREATIVE_USER_ACTION";
case SystemEvent sys when sys.duration().toMillis() > 5000 ->
"LONG_RUNNING_SYSTEM_OPERATION";
case SystemEvent sys when sys.component().equals("DATABASE") ->
"DATABASE_OPERATION";
case AuditEvent audit when !audit.success() ->
"AUDIT_FAILURE";
case ErrorEvent error when error.severity() == ErrorEvent.Severity.CRITICAL ->
"CRITICAL_FAILURE";
case ErrorEvent error when error.message().contains("timeout") ->
"TIMEOUT_ERROR";
default -> "STANDARD_EVENT";
};
}
}
Custom Deconstruction Patterns
5. Creating Custom Destructuring Methods
// Traditional class with custom deconstruction
public class ComplexNumber {
private final double real;
private final double imaginary;
public ComplexNumber(double real, double imaginary) {
this.real = real;
this.imaginary = imaginary;
}
// Traditional getters
public double getReal() { return real; }
public double getImaginary() { return imaginary; }
// Custom deconstruction method
public ComplexParts deconstruct() {
return new ComplexParts(real, imaginary);
}
// Static factory method for construction
public static ComplexNumber of(double real, double imaginary) {
return new ComplexNumber(real, imaginary);
}
// Record for deconstructed parts
public record ComplexParts(double real, double imaginary) {
public ComplexNumber toComplexNumber() {
return new ComplexNumber(real, imaginary);
}
}
@Override
public String toString() {
return String.format("%.2f + %.2fi", real, imaginary);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ComplexNumber that)) return false;
return Double.compare(that.real, real) == 0 &&
Double.compare(that.imaginary, imaginary) == 0;
}
@Override
public int hashCode() {
return Objects.hash(real, imaginary);
}
}
// Custom container with deconstruction support
public class Pair<A, B> {
private final A first;
private final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
public A first() { return first; }
public B second() { return second; }
// Deconstruction method
public Object[] deconstruct() {
return new Object[]{first, second};
}
// Pattern matching support method
public boolean matches(Class<A> firstType, Class<B> secondType) {
return firstType.isInstance(first) && secondType.isInstance(second);
}
@Override
public String toString() {
return String.format("Pair[%s, %s]", first, second);
}
// Static factory
public static <A, B> Pair<A, B> of(A first, B second) {
return new Pair<>(first, second);
}
}
// Custom deconstruction examples
public class CustomDeconstruction {
public static void processComplexNumber(ComplexNumber cn) {
// Traditional approach
double real = cn.getReal();
double imaginary = cn.getImaginary();
// Using custom deconstruction
ComplexNumber.ComplexParts parts = cn.deconstruct();
double r = parts.real();
double i = parts.imaginary();
System.out.printf("Complex number: %.2f + %.2fi%n", r, i);
}
public static void processPair(Pair<?, ?> pair) {
Object[] components = pair.deconstruct();
Object first = components[0];
Object second = components[1];
System.out.printf("Pair contains: %s and %s%n", first, second);
// Type-safe processing with pattern matching simulation
if (pair.matches(String.class, Integer.class)) {
@SuppressWarnings("unchecked")
Pair<String, Integer> stringIntPair = (Pair<String, Integer>) pair;
processStringIntPair(stringIntPair);
}
}
private static void processStringIntPair(Pair<String, Integer> pair) {
String name = pair.first();
Integer value = pair.second();
System.out.printf("String-Integer pair: %s -> %d%n", name, value);
}
// Advanced deconstruction with multiple return values
public static class DataPoint {
private final String label;
private final double[] values;
private final Map<String, Object> metadata;
public DataPoint(String label, double[] values, Map<String, Object> metadata) {
this.label = label;
this.values = values.clone(); // Defensive copy
this.metadata = new HashMap<>(metadata);
}
// Multiple deconstruction options
public BasicComponents deconstructBasic() {
return new BasicComponents(label, values.length);
}
public FullComponents deconstructFull() {
return new FullComponents(label, values, metadata);
}
public record BasicComponents(String label, int valueCount) {}
public record FullComponents(String label, double[] values, Map<String, Object> metadata) {}
public String label() { return label; }
public double[] values() { return values.clone(); } // Defensive copy
public Map<String, Object> metadata() { return new HashMap<>(metadata); }
}
}
Advanced Nesting Patterns
6. Complex Nested Pattern Matching
// Complex domain model for e-commerce
public record Customer(
String id,
String name,
Address address,
List<Order> orders,
CustomerPreferences preferences
) {
public Customer {
if (orders == null) orders = new ArrayList<>();
if (preferences == null) preferences = new CustomerPreferences();
}
public boolean hasOrders() {
return !orders.isEmpty();
}
}
public record CustomerPreferences(
boolean emailNotifications,
boolean smsNotifications,
String currency,
String language,
List<String> favoriteCategories
) {
public CustomerPreferences {
if (favoriteCategories == null) favoriteCategories = new ArrayList<>();
if (currency == null) currency = "USD";
if (language == null) language = "en";
}
}
public record Order(
String orderId,
Instant orderDate,
List<OrderItem> items,
OrderStatus status,
PaymentInfo payment,
ShippingInfo shipping
) {
public double totalAmount() {
return items.stream()
.mapToDouble(item -> item.price() * item.quantity())
.sum();
}
public boolean isShippable() {
return status == OrderStatus.CONFIRMED || status == OrderStatus.PROCESSING;
}
}
public record OrderItem(
String productId,
String productName,
int quantity,
double price,
Map<String, String> attributes
) {
public double subtotal() {
return price * quantity;
}
}
public enum OrderStatus {
PENDING, CONFIRMED, PROCESSING, SHIPPED, DELIVERED, CANCELLED
}
public record PaymentInfo(
String paymentMethod,
String transactionId,
boolean paid,
Instant paymentDate
) {}
public record ShippingInfo(
Address shippingAddress,
String carrier,
String trackingNumber,
Instant estimatedDelivery
) {}
// Advanced nested pattern matching
public class NestedPatternMatching {
// Process customer with nested pattern matching simulation
public static void analyzeCustomer(Customer customer) {
// Extract nested data
String customerName = customer.name();
Address address = customer.address();
List<Order> orders = customer.orders();
CustomerPreferences prefs = customer.preferences();
// Process address
String city = address.city();
String country = address.country();
// Process preferences
boolean wantsEmails = prefs.emailNotifications();
String preferredCurrency = prefs.currency();
System.out.printf("Customer %s in %s, %s%n", customerName, city, country);
System.out.printf("Preferences: email=%s, currency=%s%n", wantsEmails, preferredCurrency);
// Process orders with nested data
for (Order order : orders) {
processOrder(order);
}
}
private static void processOrder(Order order) {
String orderId = order.orderId();
OrderStatus status = order.status();
double total = order.totalAmount();
List<OrderItem> items = order.items();
PaymentInfo payment = order.payment();
ShippingInfo shipping = order.shipping();
System.out.printf("Order %s: %s, Total: $%.2f%n", orderId, status, total);
// Process payment info
if (payment.paid()) {
System.out.printf(" Paid via %s on %s%n",
payment.paymentMethod(), payment.paymentDate());
}
// Process shipping if applicable
if (order.isShippable() && shipping != null) {
System.out.printf(" Shipping to %s via %s%n",
shipping.shippingAddress().city(), shipping.carrier());
}
// Process items
for (OrderItem item : items) {
processOrderItem(item);
}
}
private static void processOrderItem(OrderItem item) {
String productName = item.productName();
int quantity = item.quantity();
double price = item.price();
double subtotal = item.subtotal();
System.out.printf(" %d x %s @ $%.2f = $%.2f%n",
quantity, productName, price, subtotal);
}
// Complex analysis with multiple levels of nesting
public static CustomerReport generateCustomerReport(Customer customer) {
String customerId = customer.id();
String customerName = customer.name();
Address address = customer.address();
List<Order> orders = customer.orders();
// Calculate statistics
long totalOrders = orders.size();
double totalSpent = orders.stream()
.mapToDouble(Order::totalAmount)
.sum();
double averageOrderValue = totalOrders > 0 ? totalSpent / totalOrders : 0;
// Analyze order status distribution
Map<OrderStatus, Long> statusCount = orders.stream()
.collect(Collectors.groupingBy(Order::status, Collectors.counting()));
// Find most expensive order
Optional<Order> mostExpensiveOrder = orders.stream()
.max(Comparator.comparing(Order::totalAmount));
return new CustomerReport(
customerId,
customerName,
address.city(),
address.country(),
totalOrders,
totalSpent,
averageOrderValue,
statusCount,
mostExpensiveOrder.map(Order::totalAmount).orElse(0.0)
);
}
public record CustomerReport(
String customerId,
String customerName,
String city,
String country,
long totalOrders,
double totalSpent,
double averageOrderValue,
Map<OrderStatus, Long> orderStatusDistribution,
double mostExpensiveOrder
) {
public void print() {
System.out.printf("Customer Report: %s (%s)%n", customerName, customerId);
System.out.printf("Location: %s, %s%n", city, country);
System.out.printf("Orders: %d, Total Spent: $%.2f, Avg Order: $%.2f%n",
totalOrders, totalSpent, averageOrderValue);
System.out.println("Order Status Distribution:");
orderStatusDistribution.forEach((status, count) ->
System.out.printf(" %s: %d%n", status, count));
System.out.printf("Most Expensive Order: $%.2f%n", mostExpensiveOrder);
}
}
// Pattern matching with collections
public static void processCustomerOrders(List<Customer> customers) {
for (Customer customer : customers) {
// Extract customer data
String customerName = customer.name();
List<Order> orders = customer.orders();
// Filter and process orders
List<Order> recentOrders = orders.stream()
.filter(order -> order.orderDate().isAfter(Instant.now().minusSeconds(30 * 24 * 60 * 60)))
.collect(Collectors.toList());
List<Order> highValueOrders = recentOrders.stream()
.filter(order -> order.totalAmount() > 1000)
.collect(Collectors.toList());
// Process high-value orders
for (Order order : highValueOrders) {
processHighValueOrder(customerName, order);
}
}
}
private static void processHighValueOrder(String customerName, Order order) {
System.out.printf("High value order from %s: %s - $%.2f%n",
customerName, order.orderId(), order.totalAmount());
// Check if order needs special shipping
if (order.totalAmount() > 5000) {
System.out.println(" -> Requires premium shipping");
}
}
}
Real-World Use Cases
7. Practical Application Examples
// API Response processing with pattern matching
public class ApiResponseProcessor {
public sealed interface ApiResponse<T>
permits Success, Error, Loading, Empty {
boolean isSuccess();
default boolean isError() { return !isSuccess(); }
}
public record Success<T>(T data, String message, int statusCode) implements ApiResponse<T> {
@Override
public boolean isSuccess() { return true; }
}
public record Error<T>(String errorCode, String message, Throwable cause) implements ApiResponse<T> {
@Override
public boolean isSuccess() { return false; }
}
public record Loading<T>(String progressMessage) implements ApiResponse<T> {
@Override
public boolean isSuccess() { return false; }
}
public record Empty<T>() implements ApiResponse<T> {
@Override
public boolean isSuccess() { return false; }
}
// Process API responses with pattern matching
public static <T> void handleApiResponse(ApiResponse<T> response) {
switch (response) {
case Success<T> success -> {
T data = success.data();
String message = success.message();
int statusCode = success.statusCode();
System.out.printf("Success (%d): %s%n", statusCode, message);
processSuccessData(data);
}
case Error<T> error -> {
String errorCode = error.errorCode();
String errorMessage = error.message();
Throwable cause = error.cause();
System.err.printf("Error %s: %s%n", errorCode, errorMessage);
if (cause != null) {
cause.printStackTrace();
}
}
case Loading<T> loading -> {
String progress = loading.progressMessage();
System.out.println("Loading: " + progress);
}
case Empty<T> empty -> {
System.out.println("No data available");
}
}
}
private static <T> void processSuccessData(T data) {
// Type-specific processing based on data type
if (data instanceof List<?> list) {
System.out.println("Received list with " + list.size() + " items");
} else if (data instanceof Map<?, ?> map) {
System.out.println("Received map with " + map.size() + " entries");
} else if (data instanceof String str) {
System.out.println("Received string: " + str);
} else {
System.out.println("Received data: " + data);
}
}
// Process multiple API responses
public static void processMultipleResponses(List<ApiResponse<?>> responses) {
for (ApiResponse<?> response : responses) {
if (response instanceof Success<?> success) {
Object data = success.data();
System.out.println("Processing successful response with data: " + data);
} else if (response instanceof Error<?> error) {
System.err.println("Error encountered: " + error.message());
}
}
}
}
// Configuration processing with deconstruction
public class ConfigurationProcessor {
public record DatabaseConfig(
String url,
String username,
String password,
int poolSize,
Map<String, String> properties
) {
public DatabaseConfig {
if (poolSize <= 0) {
throw new IllegalArgumentException("Pool size must be positive");
}
if (properties == null) {
properties = new HashMap<>();
}
}
}
public record ServerConfig(
String host,
int port,
boolean sslEnabled,
int maxConnections,
List<String> allowedOrigins
) {
public ServerConfig {
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Invalid port number");
}
if (allowedOrigins == null) {
allowedOrigins = new ArrayList<>();
}
}
}
public record AppConfig(
String name,
String version,
DatabaseConfig database,
ServerConfig server,
Map<String, Object> features
) {
public AppConfig {
if (features == null) {
features = new HashMap<>();
}
}
}
public static void validateAndProcessConfig(AppConfig config) {
// Deconstruct configuration
String appName = config.name();
String version = config.version();
DatabaseConfig dbConfig = config.database();
ServerConfig serverConfig = config.server();
Map<String, Object> features = config.features();
// Validate database configuration
validateDatabaseConfig(dbConfig);
// Validate server configuration
validateServerConfig(serverConfig);
// Process features
processFeatures(features);
System.out.printf("Configuration validated for %s v%s%n", appName, version);
}
private static void validateDatabaseConfig(DatabaseConfig dbConfig) {
String url = dbConfig.url();
String username = dbConfig.username();
int poolSize = dbConfig.poolSize();
if (url == null || url.isBlank()) {
throw new IllegalArgumentException("Database URL is required");
}
if (username == null || username.isBlank()) {
throw new IllegalArgumentException("Database username is required");
}
if (poolSize > 100) {
System.out.println("Warning: Large connection pool size: " + poolSize);
}
}
private static void validateServerConfig(ServerConfig serverConfig) {
String host = serverConfig.host();
int port = serverConfig.port();
boolean sslEnabled = serverConfig.sslEnabled();
if (host == null || host.isBlank()) {
throw new IllegalArgumentException("Server host is required");
}
if (sslEnabled && port == 80) {
System.out.println("Warning: SSL enabled but using HTTP port 80");
}
}
private static void processFeatures(Map<String, Object> features) {
for (Map.Entry<String, Object> entry : features.entrySet()) {
String featureName = entry.getKey();
Object featureConfig = entry.getValue();
// Pattern matching on feature configuration type
if (featureConfig instanceof Boolean enabled) {
System.out.printf("Feature %s: %s%n", featureName, enabled ? "ENABLED" : "DISABLED");
} else if (featureConfig instanceof Map<?, ?> nestedConfig) {
System.out.printf("Feature %s has nested configuration with %d settings%n",
featureName, nestedConfig.size());
} else if (featureConfig instanceof List<?> list) {
System.out.printf("Feature %s has list configuration with %d items%n",
featureName, list.size());
}
}
}
}
This comprehensive guide covers Java's deconstruction patterns from basic record usage to advanced nested pattern matching, providing practical examples and real-world use cases for modern Java development.