1. 记录类的革命性意义
Java 14引入的记录类(Record)彻底改变了我们定义数据载体的方式。它不仅仅是语法糖,更是向函数式编程和不可变设计理念的重要迈进。
1.1 传统JavaBean vs 记录类
// 传统的JavaBean方式
public class Person {
private final String name;
private final int age;
private final String email;
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
// 大量的getter、equals、hashCode、toString方法...
}
// 记录类方式
public record Person(String name, int age, String email) {
// 编译器自动生成所有必要方法
}
1.2 记录类的进阶用法
// 自定义验证逻辑
public record Email(String value) {
public Email {
if (value == null || !value.contains("@")) {
throw new IllegalArgumentException("无效的邮箱地址");
}
}
}
// 静态工厂方法
public record Currency(String code, String symbol) {
public static Currency of(String code) {
return switch (code) {
case "USD" -> new Currency("USD", "$");
case "EUR" -> new Currency("EUR", "€");
case "CNY" -> new Currency("CNY", "¥");
default -> throw new IllegalArgumentException("不支持的货币代码");
};
}
}
2. 模式匹配的威力
模式匹配与记录类的结合,为Java带来了全新的数据处理范式。
2.1 instanceof模式匹配
// 传统方式
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str.length());
}
// 模式匹配方式
if (obj instanceof String str) {
System.out.println(str.length());
}
2.2 switch表达式与模式匹配
public sealed interface Shape
permits Circle, Rectangle, Triangle {
double area();
}
public record Circle(double radius) implements Shape {
public double area() {
return Math.PI * radius * radius;
}
}
public record Rectangle(double width, double height) implements Shape {
public double area() {
return width * height;
}
}
public record Triangle(double base, double height) implements Shape {
public double area() {
return base * height / 2;
}
}
public class ShapeProcessor {
public static String describe(Shape shape) {
return switch (shape) {
case Circle c -> String.format("圆形,半径: %.2f", c.radius());
case Rectangle r -> String.format("矩形,宽: %.2f, 高: %.2f",
r.width(), r.height());
case Triangle t -> String.format("三角形,底: %.2f, 高: %.2f",
t.base(), t.height());
};
}
}
3. 构建电商领域模型
我们将使用记录类和模式匹配构建一个完整的电商系统领域模型。
3.1 核心领域对象
// 值对象
public record Money(BigDecimal amount, Currency currency) {
public Money {
if (amount.compareTo(BigDecimal.ZERO) stockQuantity) {
throw new IllegalArgumentException("库存不足");
}
return new Product(id, name, description, price,
stockQuantity - quantity, status);
}
}
public record ProductId(String value) {
public ProductId {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException("商品ID不能为空");
}
}
}
public enum ProductStatus {
ACTIVE, INACTIVE, OUT_OF_STOCK, DISCONTINUED
}
3.2 订单聚合根
public record Order(
OrderId id,
CustomerId customerId,
List items,
OrderStatus status,
Money totalAmount,
LocalDateTime createdAt,
Address shippingAddress
) {
public Order {
// 紧凑构造器中的验证逻辑
if (items == null || items.isEmpty()) {
throw new IllegalArgumentException("订单必须包含商品");
}
if (totalAmount.amount().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("订单金额必须大于0");
}
}
public static Order create(CustomerId customerId,
List items,
Address shippingAddress) {
var total = items.stream()
.map(OrderItem::subtotal)
.reduce(Money::add)
.orElseThrow();
return new Order(
OrderId.generate(),
customerId,
List.copyOf(items),
OrderStatus.CREATED,
total,
LocalDateTime.now(),
shippingAddress
);
}
public Order cancel() {
if (status != OrderStatus.CREATED && status != OrderStatus.PAID) {
throw new IllegalStateException("订单无法取消");
}
return new Order(id, customerId, items, OrderStatus.CANCELLED,
totalAmount, createdAt, shippingAddress);
}
}
public record OrderItem(
ProductId productId,
String productName,
int quantity,
Money unitPrice
) {
public Money subtotal() {
return unitPrice.multiply(BigDecimal.valueOf(quantity));
}
}
4. 领域服务与业务规则
使用模式匹配实现复杂的业务规则和定价策略。
4.1 定价策略引擎
public sealed interface PricingStrategy
permits FixedDiscount, PercentageDiscount, BuyOneGetOne, TieredPricing {
Money apply(Product product, int quantity);
}
public record FixedDiscount(Money discountAmount) implements PricingStrategy {
public Money apply(Product product, int quantity) {
return product.price().multiply(BigDecimal.valueOf(quantity))
.add(discountAmount.multiply(BigDecimal.valueOf(-1)));
}
}
public record PercentageDiscount(BigDecimal percentage) implements PricingStrategy {
public Money apply(Product product, int quantity) {
var discountMultiplier = BigDecimal.ONE
.subtract(percentage.divide(BigDecimal.valueOf(100)));
return product.price().multiply(BigDecimal.valueOf(quantity))
.multiply(discountMultiplier);
}
}
public class PricingEngine {
private final List strategies;
public PricingEngine(List strategies) {
this.strategies = List.copyOf(strategies);
}
public PricingResult calculatePrice(Product product, int quantity,
CustomerType customerType) {
var basePrice = product.price().multiply(BigDecimal.valueOf(quantity));
var bestPrice = basePrice;
PricingStrategy appliedStrategy = null;
for (var strategy : strategies) {
var candidatePrice = strategy.apply(product, quantity);
if (candidatePrice.amount().compareTo(bestPrice.amount()) bestPrice.multiply(BigDecimal.valueOf(0.9));
case PREMIUM -> bestPrice.multiply(BigDecimal.valueOf(0.95));
case REGULAR -> bestPrice;
};
return new PricingResult(basePrice, finalPrice, appliedStrategy);
}
}
public record PricingResult(
Money originalPrice,
Money finalPrice,
PricingStrategy appliedStrategy
) {
public Money discountAmount() {
return originalPrice.add(finalPrice.multiply(BigDecimal.valueOf(-1)));
}
}
5. 事件驱动的领域架构
结合记录类和模式匹配构建响应式领域事件系统。
5.1 领域事件定义
public sealed interface DomainEvent
permits OrderCreated, OrderPaid, OrderShipped, OrderCancelled,
ProductStockReduced, ProductPriceChanged {
String aggregateId();
LocalDateTime occurredOn();
}
public record OrderCreated(
OrderId orderId,
CustomerId customerId,
Money totalAmount,
LocalDateTime occurredOn
) implements DomainEvent {
public OrderCreated(Order order) {
this(order.id(), order.customerId(), order.totalAmount(),
LocalDateTime.now());
}
public String aggregateId() { return orderId().value(); }
}
public record OrderPaid(
OrderId orderId,
String paymentId,
Money paidAmount,
LocalDateTime occurredOn
) implements DomainEvent {
public String aggregateId() { return orderId().value(); }
}
// 事件处理器
public class DomainEventProcessor {
private final Map<Class,
List<EventHandler>> handlers;
public DomainEventProcessor() {
this.handlers = new ConcurrentHashMap();
}
public void registerHandler(
Class eventType, EventHandler handler) {
handlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList())
.add(handler);
}
public void process(DomainEvent event) {
var eventHandlers = handlers.get(event.getClass());
if (eventHandlers != null) {
eventHandlers.forEach(handler -> handleEvent(handler, event));
}
}
@SuppressWarnings("unchecked")
private void handleEvent(
EventHandler handler, DomainEvent event) {
try {
((EventHandler) handler).handle((T) event);
} catch (Exception e) {
// 处理异常,确保不影响其他处理器
System.err.println("处理事件失败: " + e.getMessage());
}
}
}
5.2 事件处理器的模式匹配实现
public class OrderEventHandler {
public void handleDomainEvent(DomainEvent event) {
switch (event) {
case OrderCreated e -> handleOrderCreated(e);
case OrderPaid e -> handleOrderPaid(e);
case OrderShipped e -> handleOrderShipped(e);
case OrderCancelled e -> handleOrderCancelled(e);
case ProductStockReduced e -> handleStockReduced(e);
case ProductPriceChanged e -> handlePriceChanged(e);
default -> System.out.println("未知事件类型: " + event.getClass().getSimpleName());
}
}
private void handleOrderCreated(OrderCreated event) {
System.out.printf("订单创建: %s, 金额: %s%n",
event.orderId().value(), event.totalAmount());
// 发送通知、更新读模型等
}
private void handleOrderPaid(OrderPaid event) {
System.out.printf("订单支付: %s, 支付ID: %s%n",
event.orderId().value(), event.paymentId());
// 触发发货流程
}
// 其他事件处理方法...
}
6. 测试策略与验证
利用记录类的不可变特性构建可靠的测试套件。
6.1 基于属性的测试
public class OrderTest {
@Test
void orderTotalShouldEqualSumOfItems() {
// 给定
var product1 = new Product(
new ProductId("p1"), "商品1", "描述1",
new Money(new BigDecimal("100.00"), Currency.of("CNY")),
10, ProductStatus.ACTIVE
);
var product2 = new Product(
new ProductId("p2"), "商品2", "描述2",
new Money(new BigDecimal("50.00"), Currency.of("CNY")),
5, ProductStatus.ACTIVE
);
var items = List.of(
new OrderItem(product1.id(), product1.name(), 2, product1.price()),
new OrderItem(product2.id(), product2.name(), 1, product2.price())
);
// 当
var order = Order.create(new CustomerId("cust1"), items,
new Address("中国", "北京", "某区", "某街道"));
// 那么
var expectedTotal = new Money(new BigDecimal("250.00"), Currency.of("CNY"));
assertEquals(expectedTotal, order.totalAmount());
}
@Test
void orderCancellationShouldPreserveImmutability() {
var originalOrder = createSampleOrder();
var cancelledOrder = originalOrder.cancel();
// 原始订单不应被修改
assertEquals(OrderStatus.CREATED, originalOrder.status());
assertEquals(OrderStatus.CANCELLED, cancelledOrder.status());
// 其他字段应该保持不变
assertEquals(originalOrder.id(), cancelledOrder.id());
assertEquals(originalOrder.items(), cancelledOrder.items());
}
}
7. 性能优化与最佳实践
7.1 记录类的性能优势
public class RecordPerformance {
public static void main(String[] args) {
var iterations = 1_000_000;
// 测试记录类的实例创建性能
long recordStart = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
var person = new Person("姓名" + i, i % 100, "email" + i + "@test.com");
}
long recordTime = System.currentTimeMillis() - recordStart;
// 测试传统类的实例创建性能
long traditionalStart = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
var person = new TraditionalPerson("姓名" + i, i % 100, "email" + i + "@test.com");
}
long traditionalTime = System.currentTimeMillis() - traditionalStart;
System.out.printf("记录类耗时: %dms%n", recordTime);
System.out.printf("传统类耗时: %dms%n", traditionalTime);
System.out.printf("性能提升: %.2f%%%n",
(traditionalTime - recordTime) * 100.0 / traditionalTime);
}
}
7.2 内存占用分析
public class MemoryAnalysis {
public static void analyzeMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
// 记录类内存使用
runtime.gc();
long recordMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
List records = new ArrayList();
for (int i = 0; i < 100000; i++) {
records.add(new Person("Name" + i, 25, "test@example.com"));
}
long recordMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
// 传统类内存使用
runtime.gc();
long traditionalMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
List traditional = new ArrayList();
for (int i = 0; i < 100000; i++) {
traditional.add(new TraditionalPerson("Name" + i, 25, "test@example.com"));
}
long traditionalMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
System.out.printf("记录类内存占用: %d bytes%n",
recordMemoryAfter - recordMemoryBefore);
System.out.printf("传统类内存占用: %d bytes%n",
traditionalMemoryAfter - traditionalMemoryBefore);
}
}
总结
Java记录类和模式匹配的结合为现代Java开发带来了重大改进:
- 显著减少样板代码,提高开发效率
- 内置不可变性,增强线程安全性
- 模式匹配提供更清晰的条件逻辑表达
- 更好的性能表现和内存效率
- 与领域驱动设计理念完美契合
这些特性使得Java在构建复杂业务系统时更加安全、高效和可维护。开发者应该积极采用这些现代Java特性,以构建更健壮的应用程序。