السجلّات مقابل الفئات وLombok
قارن بين السجلّات والفئات التقليدية وتعليقات Lombok التوضيحية لاختيار الأداة المناسبة
السجلّات مقابل الفئات وLombok درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
السجلات مقارنةً بالفئات وLombok
توفر Java عدة طرق لإنشاء أنواع حاملة للبيانات: فئات عادية تحتوي على شيفرة نمطية، أو السجلات المتاحة منذ Java 16، أو التعليقات التوضيحية في Lombok. ويعتمد اختيار الطريقة المناسبة على حالة الاستخدام.
مشكلة الشيفرة النمطية
تتطلب فئة Java العادية التي تحمل بيانات بسيطة قدرًا كبيرًا من الشيفرة النمطية: منشئًا ودوال وصول وequals وhashCode وtoString، وكل ذلك من أجل بضعة حقول فقط.
// Traditional class — lots of boilerplate
class ProductOld {
private final String id;
private final String name;
private final double price;
public ProductOld(String id, String name, double price) {
this.id = id; this.name = name; this.price = price;
}
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
@Override public boolean equals(Object o) { /* ... */ }
@Override public int hashCode() { /* ... */ }
@Override public String toString() { /* ... */ }
}حل السجل
يختصر السجل الفئة نفسها في سطر واحد. وينشئ المصرّف جميع الشيفرة النمطية تلقائيًا.
// Record — one line, same functionality
record Product(String id, String name, double price) {}
// Usage is almost identical except accessors use component names (no get prefix)
Product p = new Product("P001", "Laptop", 999.99);
System.out.println(p.id()); // P001 (not getId())
System.out.println(p.name()); // Laptop
System.out.println(p.price()); // 999.99
System.out.println(p); // Product[id=P001, name=Laptop, price=999.99]Lombok @Data و@Value
تنشئ Lombok الشيفرة النمطية وقت الترجمة من خلال التعليقات التوضيحية. وتنشئ @Value فئة غير قابلة للتغيير تشبه السجلات، بينما تنشئ @Data فئة قابلة للتغيير.
// Lombok @Value — immutable (closest to record)
import lombok.Value;
@Value
public class LombokProduct {
String id;
String name;
double price;
// Lombok generates: all-args constructor, getters, equals, hashCode, toString
// Note: accessors follow getX() naming convention (getPrice(), not price())
}
// Lombok @Data — mutable
import lombok.Data;
@Data
public class MutableProduct {
private String id;
private String name;
private double price;
// Generates: getters, setters, equals, hashCode, toString, no-args constructor
}الفروق الرئيسية
الفروق المهمة بين Records وLombok @Value والأصناف العادية:
- تسمية الموصلات: تستخدم Records الصيغة
name()، بينما يستخدم Lombok الصيغةgetName() - البديل القابل للتغيير: تكون Records غير قابلة للتغيير دائمًا، بينما يوفر Lombok التعليمة @Data لإتاحة التغيير
- الوراثة: لا يمكن لـ Records أن ترث من أصناف، بينما يمكن لأصناف Lombok ذلك
- الاعتماد على أداة البناء: يتطلب Lombok إعداد annotation processor، بينما تكون Records مدمجة في Java
متى تستخدم Records
تكون Records مثالية عندما:
- يكون النوع في الأساس حاملاً شفافًا للبيانات (DTO أو Value Object)
- تكون عدم قابلية التغيير مطلوبةً حسب التصميم
- تستخدم Java 16 أو إصدارًا أحدث وتريد تجنب الاعتماديات
- يشارك النوع في مطابقة الأنماط (sealed + records)
// Perfect record use cases:
record Coordinates(double lat, double lon) {}
record JwtClaims(String subject, String role, long expiresAt) {}
record PageRequest(int page, int size, String sortBy) {}
record ErrorResponse(int code, String message, String path) {}
// These are all simple data carriers with no mutable stateمتى تستخدم Lombok
يُفضَّل استخدام Lombok عندما:
- تحتاج إلى JavaBeans قابلة للتغيير (مثل كيانات JPA أو أصناف إعدادات Spring)
- يجب أن تتبع تسمية الموصلات اصطلاح JavaBeans (
getX) لضمان التوافق مع أطر العمل - تحتاج إلى
@Builderلإنشاء كائنات معقدة - تحتاج إلى قابلية تغيير جزئية (
@NonFinalلبعض الحقول)
// Lombok @Builder — great for complex object construction
import lombok.*;
@Builder
@Value
public class EmailMessage {
String to;
String from;
String subject;
String body;
List<String> attachments;
}
EmailMessage msg = EmailMessage.builder()
.to("user@example.com")
.from("noreply@app.com")
.subject("Welcome!")
.body("Thank you for signing up")
.attachments(List.of())
.build();متى تستخدم الأصناف العادية
تظل الأصناف العادية مناسبةً عندما:
- يحتوي الصنف على سلوك معقد يتجاوز مجرد حمل البيانات
- تحتاج إلى الوراثة من صنف آخر
- تحتاج إلى تحكم دقيق في hashCode وequals
- تحتاج إلى حالة قابلة للتغيير مع الحفاظ على التغليف
// Plain class — when behavior dominates over data
class ShoppingCart {
private final List<CartItem> items = new ArrayList<>();
private final String customerId;
public ShoppingCart(String customerId) {
this.customerId = customerId;
}
public void addItem(CartItem item) { items.add(item); }
public void removeItem(String sku) { items.removeIf(i -> i.sku().equals(sku)); }
public double total() { return items.stream().mapToDouble(CartItem::lineTotal).sum(); }
public boolean isEmpty() { return items.isEmpty(); }
}الدمج بين Records والأصناف
تعمل Records والأصناف معًا بسلاسة. استخدم Records للبيانات غير القابلة للتغيير داخل الأصناف التي تدير حالة قابلة للتغيير.
// Record for the immutable event data
record UserEvent(String userId, String action, java.time.Instant timestamp) {
public static UserEvent now(String userId, String action) {
return new UserEvent(userId, action, java.time.Instant.now());
}
}
// Class for the mutable event store
class EventLog {
private final List<UserEvent> events = new ArrayList<>();
public void record(String userId, String action) {
events.add(UserEvent.now(userId, action));
}
public List<UserEvent> eventsFor(String userId) {
return events.stream()
.filter(e -> e.userId().equals(userId)).toList();
}
}Records في Spring Boot
تتكامل Records جيدًا مع Spring Boot بصفتها أجسامًا لطلبات واستجابات API. ويتعامل Jackson mapper الخاص بـ Spring معها بأقل قدر من الإعداد.
// Spring Boot REST controller using records
import org.springframework.web.bind.annotation.*;
record CreateProductRequest(String name, double price, String category) {}
record ProductResponse(long id, String name, double price, String category) {}
@RestController
@RequestMapping("/api/products")
class ProductController {
@PostMapping
public ProductResponse create(@RequestBody CreateProductRequest req) {
// Jackson automatically deserializes JSON into the record
long id = productService.save(req);
return new ProductResponse(id, req.name(), req.price(), req.category());
}
}مقارنة الأداء
تنتج الأساليب الثلاثة عمليًا نفس bytecode لحوامل البيانات البسيطة. ويكون الأداء متطابقًا، لذا يتعلق الاختيار بتجربة المطور والمتطلبات.
// All three compile to approximately equivalent bytecode:
// - Records: compiler-generated, always immutable
// - Lombok @Value: annotation-processor-generated, always immutable
// - Plain class: hand-written
// The key runtime difference:
// Records: accessor is a method matching field name (price() not getPrice())
// Lombok/Class: accessor follows JavaBeans (getPrice())
// Jackson compatibility:
// Records: work natively in Jackson 2.12+ with no config
// Lombok: work with @JsonProperty or jackson-databind-lombok moduleتحقق سريع
ما اصطلاح التسمية الذي تتبعه موصلات record؟
مراجعة: Records مقابل الأصناف وLombok
أهم النقاط:
- تنشئ Records تلقائيًا المُنشئ والموصلات (من دون السابقة get) وequals وhashCode وtoString
- تكون Records غير قابلة للتغيير دائمًا؛ استخدم الأصناف العادية للحالة القابلة للتغيير
- تقارب Lombok @Value سلوك Records، لكنها تستخدم تسمية getX وتتطلب إعدادًا
- تتفوق Lombok @Builder في إنشاء الكائنات المعقدة التي تحتوي على حقول اختيارية كثيرة
- تُعد Records مثالية لـ DTOs وvalue objects وأجسام طلبات واستجابات API
- تظل الأصناف العادية الخيار الأفضل عندما يكون السلوك هو العنصر الأهم أو تكون الوراثة مطلوبة
الأسئلة الشائعة
هل درس «السجلّات مقابل الفئات وLombok» مجاني؟
نعم — نص درس «السجلّات مقابل الفئات وLombok» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «السجلّات مقابل الفئات وLombok»؟
قارن بين السجلّات والفئات التقليدية وتعليقات Lombok التوضيحية لاختيار الأداة المناسبة تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «السجلّات مقابل الفئات وLombok»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- مقدمة إلى السجلّات
- المنشئات المختصرة والتحقق
- الأساليب المخصصة في السجلّات
- السجلّات مقابل الفئات وLombok