الـ Records (Java 16+)
جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 51 من 87.
عند إنشاء class بسيطة لحمل البيانات، غالبًا ما تكتب تعليمات برمجية نمطية متكررة: fields خاصة، وconstructor، وgetters، وtoString()، وequals()، وhashCode(). قدّمت Java 16 مفهوم السجلات للتخلّص من هذا العمل الممل.
يُعد record فئة خاصة مصممة للاحتفاظ ببيانات غير قابلة للتغيير. تُعلن عنه باستخدام الكلمة المفتاحية record متبوعة بقائمة المكوّنات:
record Person(String name, int age) { }
// يوفر هذا السطر الواحد تلقائيًا:
// - حقول private final: name و age
// - دالة بناء (constructor): Person(String name, int age)
// - دوال وصول (accessor methods): name() و age()
// - toString() و equals() و hashCode()استخدام record أمر مباشر:
Person p = new Person("Alice", 30);
System.out.println(p.name()); // Alice
System.out.println(p.age()); // 30
System.out.println(p); // Person[name=Alice, age=30]لاحظ أن أساليب accessor لا تستخدم البادئة get: بل تُسمّى ببساطة بعد fields. تكون السجلات ضمنيًا final، لذا cannot توسيعها. جميع fields هي final أيضًا، مما يجعل السجلات غير قابلة للتغيير بطبيعتها.
يمكنك إضافة custom methods وتجاوز ones المُنشأة تلقائيًا عند الحاجة. ويمكنك أيضًا إضافة التحقق في compact constructor:
record Person(String name, int age) {
public Person {
if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
}
}تُعد Records مثاليةً لـ DTOs أو استجابات API أو أي سيناريو تحتاج فيه إلى حاوية بيانات بسيطة غير قابلة للتغيير دون كتابة تعليمات برمجية نمطية.
التحدي
سهللنَبْنِ نظامًا لحجز تذاكر الأفلام يُبرز أناقة سجلات Java. سترى كيف تلغي السجلات الشيفرة المتكررة، مع الاستمرار في إتاحة إضافة custom methods والتحقق من الصحة.
ستُنظّم شيفرتك عبر ثلاثة ملفات:
Movie.java: أنشئrecordيمثّل فيلمًا بثلاثة مكوّنات:title(String)، وgenre(String)، وdurationMinutes(int). أضف method مخصّصة باسمgetFormattedDuration()تُعيد المدة بتنسيق سهل القراءة:[hours]h [minutes]m(على سبيل المثال، تصبح 142 دقيقة2h 22m). استخدم القسمة الصحيحة وباقي القسمة لحساب الساعات والدقائق المتبقية.Ticket.java: أنشئrecordيمثّل تذكرة فيلم بثلاثة مكوّنات:movie(Movie)، وseatNumber(String)، وprice(double). أضفcompact constructorيتحقق من السعر؛ فإذا كان السعر أقل من أو يساوي الصفر، فارْمِIllegalArgumentExceptionمع الرسالةPrice must be positive. أضف أيضًا method مخصّصة باسمprintTicket()تُعيد string متعدد الأسطر:========== TICKET ========== Movie: [movie title] Genre: [movie genre] Duration: [formatted duration from movie] Seat: [seatNumber] Price: $[price] ============================Main.java: اجمع نظام الحجز معًا! ستتلقى خمسة inputs: عنوان الفيلم (String)، وgenre (String)، والمدة بالدقائق (int)، ورقم المقعد (String)، وسعر التذكرة (double).أنشئ Movie record باستخدام أول ثلاثة inputs، ثم أنشئ Ticket record باستخدام الفيلم ورقم المقعد والسعر. اطبع نتيجة استدعاء
printTicket()على تذكرتك.
ستتلقى خمسة inputs بالترتيب: عنوان الفيلم، وgenre، والمدة (بالدقائق)، ورقم المقعد، والسعر.
لاحظ مدى نظافة السجلات مقارنةً بـ classes التقليدية: فلا حاجة إلى كتابة constructors أو getters أو toString() أو equals() أو hashCode(). يتيح لك compact constructor إضافة التحقق من الصحة دون تكرار تعيينات fields، ولا يزال بإمكانك إضافة methods مخصّصة عند الحاجة!
جرّب بنفسك
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// قراءة المدخلات
String title = scanner.nextLine();
String genre = scanner.nextLine();
int durationMinutes = Integer.parseInt(scanner.nextLine());
String seatNumber = scanner.nextLine();
double price = Double.parseDouble(scanner.nextLine());
// TODO: إنشاء سجل Movie يحتوي على title و genre و durationMinutes
// TODO: إنشاء سجل Ticket يحتوي على movie و seatNumber و price
// TODO: طباعة التذكرة باستخدام الدالة printTicket()
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت