Java14 Records案例

wen java案例 1

本文目录导读:

Java14 Records案例

  1. Java 14 Records 完整案例
  2. 注意事项

Java 14 Records 完整案例

Records 是 Java 14 引入的预览特性,用于创建不可变的数据载体类,下面通过多个案例详细展示其用法。

基础用法

// 最简单的 Record 定义
public record Point(int x, int y) {}
// 使用示例
public class BasicRecordExample {
    public static void main(String[] args) {
        Point p1 = new Point(1, 2);
        Point p2 = new Point(1, 2);
        // 自动生成的方法
        System.out.println("x: " + p1.x());  // 访问器方法
        System.out.println("y: " + p1.y());
        System.out.println("toString: " + p1);
        System.out.println("hashCode: " + p1.hashCode());
        System.out.println("equals: " + p1.equals(p2));  // true
        // 解构(Java 21+ 支持)
        // int x = p1.x();
        // int y = p1.y();
    }
}

带自定义构造器的 Record

// 带验证逻辑的 Record
public record Person(String name, int age) {
    // 紧凑构造器(Compact Constructor)
    public Person {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Age must be between 0 and 150");
        }
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("Name cannot be null or blank");
        }
        // 可以修改参数值
        name = name.trim();
    }
    // 额外的静态工厂方法
    public static Person of(String fullName) {
        String[] parts = fullName.split(" ");
        return new Person(parts[0], Integer.parseInt(parts[1]));
    }
    // 额外的实例方法
    public boolean isAdult() {
        return age >= 18;
    }
    // 重写访问器
    @Override
    public String name() {
        return "Name: " + name;
    }
}

包含集合的 Record

import java.util.ArrayList;
import java.util.List;
// 注意:Record 的字段是 final,但集合内容可变
public record Course(String name, List<String> students) {
    // 防御性拷贝构造函数
    public Course {
        students = List.copyOf(students);  // 返回不可变列表
    }
    // 添加学生(返回新实例)
    public Course addStudent(String student) {
        List<String> newStudents = new ArrayList<>(students);
        newStudents.add(student);
        return new Course(name, newStudents);
    }
}
public class RecordWithCollection {
    public static void main(String[] args) {
        Course course = new Course("Java Programming", 
            new ArrayList<>(List.of("Alice", "Bob")));
        // 尝试修改集合会抛出异常
        try {
            course.students().add("Charlie");
        } catch (UnsupportedOperationException e) {
            System.out.println("Cannot modify immutable list");
        }
        // 通过方法添加
        Course updated = course.addStudent("Charlie");
        System.out.println(updated);
    }
}

Record 与泛型

// 泛型 Record
public record Pair<K, V>(K key, V value) {
    static <K, V> Pair<K, V> of(K key, V value) {
        return new Pair<>(key, value);
    }
}
// 带类型约束的 Record
public record NumberBox<T extends Number>(T value) {
    public double doubleValue() {
        return value.doubleValue();
    }
}
public class GenericRecordExample {
    public static void main(String[] args) {
        Pair<String, Integer> pair = Pair.of("age", 25);
        System.out.println(pair);
        NumberBox<Integer> intBox = new NumberBox<>(42);
        System.out.println("Double value: " + intBox.doubleValue());
        // NumberBox<String> box = new NumberBox<>("test"); // 编译错误
    }
}

Record 实现接口

// 定义接口
interface Shape {
    double area();
    default String describe() {
        return "Shape with area: " + area();
    }
}
// Record 实现接口
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 String describe() {
        return "Circle with radius " + radius + ", area: " + area();
    }
}
public record Rectangle(double width, double height) implements Shape {
    @Override
    public double area() {
        return width * height;
    }
}
// 使用示例
public class RecordInterfaceExample {
    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        Shape rectangle = new Rectangle(4.0, 6.0);
        System.out.println(circle.describe());
        System.out.println(rectangle.describe());
        // 多态使用
        List<Shape> shapes = List.of(circle, rectangle);
        shapes.forEach(s -> System.out.println("Area: " + s.area()));
    }
}

Record 与序列化

import java.io.*;
// Record 天然支持序列化
public record Employee(String id, String name, String department) 
    implements Serializable {
    // 版本序列化 ID
    private static final long serialVersionUID = 1L;
    // 序列化时的验证
    public Employee {
        if (id == null || id.isBlank()) {
            throw new IllegalArgumentException("ID cannot be empty");
        }
    }
}
public class RecordSerializationExample {
    public static void main(String[] args) throws Exception {
        Employee employee = new Employee("001", "张三", "IT");
        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("employee.ser"))) {
            oos.writeObject(employee);
        }
        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("employee.ser"))) {
            Employee deserialized = (Employee) ois.readObject();
            System.out.println("Deserialized: " + deserialized);
            System.out.println("Equals original: " + 
                deserialized.equals(employee));
        }
    }
}

实际应用场景 - 数据传输对象(DTO)

import java.time.LocalDateTime;
import java.util.Map;
// API 响应 DTO
public record ApiResponse<T>(
    int status,
    String message,
    T data,
    LocalDateTime timestamp
) {
    // 静态工厂方法
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "Success", data, LocalDateTime.now());
    }
    public static <T> ApiResponse<T> error(int status, String message) {
        return new ApiResponse<>(status, message, null, LocalDateTime.now());
    }
    // 检查是否成功
    public boolean isSuccess() {
        return status >= 200 && status < 300;
    }
}
// 用户请求 DTO
public record UserCreateRequest(
    String username,
    String email,
    String password,
    Map<String, String> metadata
) {
    // 紧凑构造器验证
    public UserCreateRequest {
        if (username == null || username.isBlank()) {
            throw new IllegalArgumentException("Username is required");
        }
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
        if (password == null || password.length() < 8) {
            throw new IllegalArgumentException("Password too short");
        }
        // 防御性拷贝
        metadata = metadata == null ? Map.of() : Map.copyOf(metadata);
    }
    // 脱敏密码方法
    public String maskedPassword() {
        return "*".repeat(password.length());
    }
}
// 使用示例
public class DTOExample {
    public static void main(String[] args) {
        // 创建用户请求
        UserCreateRequest request = new UserCreateRequest(
            "john_doe",
            "john@example.com",
            "password123",
            Map.of("role", "admin")
        );
        System.out.println("Username: " + request.username());
        System.out.println("Password: " + request.maskedPassword());
        // 创建响应
        ApiResponse<UserCreateRequest> success = 
            ApiResponse.success(request);
        ApiResponse<String> error = 
            ApiResponse.error(400, "Bad Request");
        System.out.println(success);
        System.out.println("Is success: " + success.isSuccess());
        System.out.println(error);
    }
}

Record 与 Stream 结合使用

import java.util.List;
import java.util.stream.Collectors;
public record Product(String name, double price, String category) {}
public class RecordWithStream {
    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("Laptop", 1299.99, "Electronics"),
            new Product("Keyboard", 99.99, "Electronics"),
            new Product("Book", 19.99, "Books"),
            new Product("Monitor", 499.99, "Electronics"),
            new Product("Headphones", 199.99, "Accessories")
        );
        // 分组统计
        Map<String, List<Product>> byCategory = products.stream()
            .collect(Collectors.groupingBy(Product::category));
        System.out.println("Products by category:");
        byCategory.forEach((cat, list) -> 
            System.out.println(cat + ": " + list.size() + " items"));
        // 过滤和转换
        List<String> expensiveProductNames = products.stream()
            .filter(p -> p.price() > 100)
            .map(Product::name)
            .sorted()
            .collect(Collectors.toList());
        System.out.println("\nExpensive products: " + expensiveProductNames);
        // 计算平均价格
        double avgPrice = products.stream()
            .mapToDouble(Product::price)
            .average()
            .orElse(0);
        System.out.println("Average price: $" + String.format("%.2f", avgPrice));
    }
}

嵌套 Record

// 嵌套 Record 示例
public record Order(
    String orderId,
    Customer customer,
    Address shippingAddress,
    List<OrderItem> items
) {
    // 嵌套的 Record
    public record Customer(String id, String name, String email) {}
    public record Address(String street, String city, String zipCode) {}
    public record OrderItem(String productId, int quantity, double price) {
        public double totalPrice() {
            return price * quantity;
        }
    }
    // 计算订单总额
    public double totalAmount() {
        return items.stream()
            .mapToDouble(OrderItem::totalPrice)
            .sum();
    }
    // 自定义 toString
    @Override
    public String toString() {
        return String.format("Order[%s] - Customer: %s - Total: $%.2f",
            orderId, customer().name(), totalAmount());
    }
}
public class NestedRecordExample {
    public static void main(String[] args) {
        Order.Customer customer = new Order.Customer("C001", "Alice", "alice@email.com");
        Order.Address address = new Order.Address("123 Main St", "Springfield", "12345");
        List<Order.OrderItem> items = List.of(
            new Order.OrderItem("P001", 2, 25.99),
            new Order.OrderItem("P002", 1, 99.99)
        );
        Order order = new Order("ORD-2024-001", customer, address, items);
        System.out.println(order);
        System.out.println("Total: $" + String.format("%.2f", order.totalAmount()));
    }
}

Record 的最佳实践示例

import java.util.Objects;
import java.util.regex.Pattern;
// 综合示例:展示 Record 的最佳实践
public class BestPracticesExample {
    // 1. 使用静态工厂方法
    public record Email(String value) {
        private static final Pattern EMAIL_PATTERN = 
            Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$");
        public Email {
            Objects.requireNonNull(value, "Email cannot be null");
            if (!EMAIL_PATTERN.matcher(value).matches()) {
                throw new IllegalArgumentException("Invalid email: " + value);
            }
            value = value.toLowerCase();
        }
        // 静态工厂
        public static Email of(String value) {
            return new Email(value);
        }
    }
    // 2. 使用记录作为配置对象
    public record AppConfig(
        String appName,
        int port,
        boolean debug,
        String databaseUrl
    ) {
        // 默认配置
        public static AppConfig defaultConfig() {
            return new AppConfig("MyApp", 8080, false, "jdbc:mysql://localhost:3306/mydb");
        }
        // 从环境变量创建
        public static AppConfig fromEnv() {
            return new AppConfig(
                System.getenv().getOrDefault("APP_NAME", "MyApp"),
                Integer.parseInt(System.getenv().getOrDefault("PORT", "8080")),
                Boolean.parseBoolean(System.getenv().getOrDefault("DEBUG", "false")),
                System.getenv().getOrDefault("DB_URL", "jdbc:mysql://localhost:3306/mydb")
            );
        }
    }
    // 3. 使用 Record 表示不可变的数据结构
    public record Money(String currency, BigDecimal amount) {
        public Money {
            if (amount.compareTo(BigDecimal.ZERO) < 0) {
                throw new IllegalArgumentException("Amount cannot be negative");
            }
        }
        public Money add(Money other) {
            if (!currency.equals(other.currency)) {
                throw new IllegalArgumentException("Currency mismatch");
            }
            return new Money(currency, amount.add(other.amount));
        }
        @Override
        public String toString() {
            return String.format("%s %.2f", currency, amount);
        }
    }
    public static void main(String[] args) {
        // 测试 Email
        Email email = Email.of("User@Example.com");
        System.out.println("Valid email: " + email.value());
        // 测试配置
        AppConfig config = AppConfig.defaultConfig();
        System.out.println("Config: " + config);
        // 测试 Money
        Money price1 = new Money("USD", new BigDecimal("19.99"));
        Money price2 = new Money("USD", new BigDecimal("4.99"));
        Money total = price1.add(price2);
        System.out.println("Price 1: " + price1);
        System.out.println("Price 2: " + price2);
        System.out.println("Total: " + total);
    }
}

注意事项

  1. Record 的字段是 final 的,只能在构造器中赋值
  2. Record 是隐式 final 的,不能被继承
  3. Record 不能声明实例字段,但可以声明静态字段
  4. Record 自动生成 equals(), hashCode(), toString(), 访问器方法
  5. 紧凑构造器可以修改参数值
  6. 可以对 Record 进行防御性拷贝来保护不可变性
  7. Record 适合作为 DTO、值对象、配置对象等

这些案例涵盖了 Java 14 Records 的主要使用场景和最佳实践。

抱歉,评论功能暂时关闭!