الأساليب المخصصة في السجلّات
أضف أساليب الكائن وأساليب المصنع الثابتة لتعزيز وظائف السجلّات
الأساليب المخصصة في السجلّات درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
الدوال المخصصة في السجلات
يمكن أن تحتوي السجلات على دوال مثيلات ودوال ثابتة وحقول ثابتة، أي سلوك يُشتق من مكوّنات السجل أو يرتبط بها.
دوال المثيلات في السجلات
أضيفوا دوال إلى السجلات لحساب القيم المشتقة أو توفير عمليات مفيدة تستند إلى المكوّنات.
record Money(long cents, String currency) {
public double amount() { return cents / 100.0; }
public String display() { return String.format("%s %.2f", currency, amount()); }
public Money add(Money other) {
if (!currency.equals(other.currency))
throw new IllegalArgumentException("Currency mismatch");
return new Money(cents + other.cents, currency);
}
public Money multiply(int factor) { return new Money(cents * factor, currency); }
}
Money price = new Money(1999, "USD");
Money tax = new Money(160, "USD");
System.out.println(price.add(tax).display()); // USD 21.59دوال المصنع الثابتة
توفر دوال المصنع الثابتة طرقًا مُسمّاة وواضحة لإنشاء السجلات، إذ تخفي المنشئ الخام عندما يكون أقل وضوحًا.
import java.time.*;
record DateRange(LocalDate start, LocalDate end) {
DateRange { if (start.isAfter(end)) throw new IllegalArgumentException(); }
public static DateRange of(LocalDate start, LocalDate end) {
return new DateRange(start, end);
}
public static DateRange thisMonth() {
LocalDate now = LocalDate.now();
return new DateRange(now.withDayOfMonth(1),
now.withDayOfMonth(now.lengthOfMonth()));
}
public long durationDays() {
return ChronoUnit.DAYS.between(start, end) + 1;
}
}
System.out.println(DateRange.thisMonth().durationDays());دوال التحقق المنطقي
توفّر السجلات عادةً دوال تحقق منطقية لفحص الشروط المتعلقة بالبيانات التي تحتفظ بها.
record User(String email, String plan, boolean emailVerified) {
public boolean isPro() { return "PRO".equals(plan); }
public boolean canPublish() { return isPro() && emailVerified; }
public boolean isFree() { return "FREE".equals(plan); }
public User withPlan(String newPlan) {
return new User(email, newPlan, emailVerified); // "update"
}
}
User u = new User("alice@example.com", "FREE", true);
System.out.println(u.canPublish()); // false
User upgraded = u.withPlan("PRO");
System.out.println(upgraded.canPublish()); // trueدوال Wither (النسخ مع التعديل)
بما أن السجلات غير قابلة للتغيير، فإن إجراء تحديث يعني إنشاء سجل جديد. وتتبع دوال Wither النمط withX(newValue).
record ServerConfig(String host, int port, boolean ssl, int timeout) {
public ServerConfig withHost(String h) { return new ServerConfig(h, port, ssl, timeout); }
public ServerConfig withPort(int p) { return new ServerConfig(host, p, ssl, timeout); }
public ServerConfig withSsl(boolean s) { return new ServerConfig(host, port, s, timeout); }
public ServerConfig withTimeout(int t) { return new ServerConfig(host, port, ssl, t); }
}
ServerConfig dev = new ServerConfig("localhost", 8080, false, 30);
ServerConfig prod = dev
.withHost("api.example.com")
.withPort(443)
.withSsl(true)
.withTimeout(60);
System.out.println(prod);السجلات التي تطبّق الواجهات الوظيفية
يمكن تمرير سجل يطبّق أيضًا واجهة وظيفية مباشرةً بوصفه بديلًا عن lambda.
import java.util.function.*;
record Validator<T>(Predicate<T> rule, String message)
implements Predicate<T> {
public boolean test(T value) { return rule.test(value); }
public String validate(T value) {
return test(value) ? null : message;
}
}
Validator<String> notEmpty = new Validator<>(
s -> !s.isBlank(), "Must not be blank");
Validator<String> validEmail = new Validator<>(
s -> s.contains("@"), "Invalid email");
System.out.println(notEmpty.validate("")); // Must not be blank
System.out.println(validEmail.validate("a@b.c")); // null (valid)دوال التجميع
يمكن للسجلات التي تحتفظ بمجموعات توفير دوال تجميع تستند إلى stream.
import java.util.*;
record SalesReport(String region, List<Double> dailySales) {
SalesReport { dailySales = List.copyOf(dailySales); }
public double total() { return dailySales.stream().mapToDouble(Double::doubleValue).sum(); }
public double average() { return dailySales.stream().mapToDouble(Double::doubleValue).average().orElse(0); }
public double max() { return dailySales.stream().mapToDouble(Double::doubleValue).max().orElse(0); }
public int days() { return dailySales.size(); }
}
SalesReport r = new SalesReport("North", List.of(1200.0, 1450.0, 980.0, 1600.0));
System.out.printf("Total: %.0f, Avg: %.0f, Max: %.0f%n", r.total(), r.average(), r.max());
// Total: 5230, Avg: 1307, Max: 1600تجاوز toString مخصص
رغم أن toString المُنشأ تلقائيًا مفيد لتصحيح الأخطاء، قد ترغبون في تجاوزه لعرض البيانات للمستخدم.
record Temperature(double celsius) {
@Override
public String toString() {
return String.format("%.1f\u00b0C / %.1f\u00b0F",
celsius, toFahrenheit());
}
public double toFahrenheit() { return celsius * 9.0/5.0 + 32; }
public double toKelvin() { return celsius + 273.15; }
public static Temperature fromFahrenheit(double f) {
return new Temperature((f - 32) * 5.0/9.0);
}
}
Temperature boiling = new Temperature(100.0);
System.out.println(boiling); // 100.0°C / 212.0°Fالسجلات بوصفها كائنات قيمة في DDD
في التصميم المُوجّه بالمجال، تتوافق السجلات طبيعيًا مع كائنات القيمة، وهي أنواع غير قابلة للتغيير تُعرَّف بسماتها لا بهويتها.
// Value objects in a banking domain
record AccountId(String value) {
AccountId { if (!value.matches("ACC-[0-9]{8}")) throw new IllegalArgumentException(); }
}
record Amount(long cents, String currency) {
public static Amount usd(long cents) { return new Amount(cents, "USD"); }
public boolean isPositive() { return cents > 0; }
}
record TransactionId(java.util.UUID value) {
public static TransactionId generate() {
return new TransactionId(java.util.UUID.randomUUID());
}
}
AccountId id = new AccountId("ACC-12345678");
Amount amount = Amount.usd(5000);
TransactionId txId = TransactionId.generate();
System.out.println(id + " " + amount + " " + txId);مقارنة السجلات باستخدام Comparator
يمكن ترتيب السجلات باستخدام Comparator.comparing مع مراجع دوال تشير إلى دوال الوصول.
import java.util.*;
record Employee(String name, String dept, double salary) {}
List<Employee> employees = List.of(
new Employee("Alice", "Engineering", 95000),
new Employee("Bob", "Marketing", 72000),
new Employee("Charlie", "Engineering", 105000),
new Employee("Diana", "Marketing", 81000)
);
// Sort by dept, then salary descending
employees.stream()
.sorted(Comparator.comparing(Employee::dept)
.thenComparing(Comparator.comparingDouble(Employee::salary).reversed()))
.forEach(e -> System.out.printf("%-10s %-15s %.0f%n",
e.name(), e.dept(), e.salary()));دوال الأدوات الثابتة في السجلات
يمكن أن تحتوي السجلات على دوال ثابتة تعمل مع مثيلات متعددة من السجلات، ما يفيد في التحويلات والمقارنات.
import java.util.*;
import java.util.stream.*;
record Product(String id, String category, double price) {
public static Map<String, DoubleSummaryStatistics> statsByCategory(
List<Product> products) {
return products.stream().collect(
Collectors.groupingBy(Product::category,
Collectors.summarizingDouble(Product::price)));
}
public static Optional<Product> cheapest(List<Product> products) {
return products.stream()
.min(Comparator.comparingDouble(Product::price));
}
}اختبار سريع
تريدون تحديث حقل في سجل. ما النهج الصحيح؟
مراجعة: الدوال المخصصة في السجلات
أهم النقاط:
- يمكن أن تحتوي السجلات على دوال مثيلات ودوال ثابتة وحقول ثابتة
- أضيفوا الخصائص المحسوبة بوصفها دوال، مثل area() وtotal() وdisplay()
- استخدموا دوال المصنع الثابتة لإنشاء منشئات مُسمّاة واضحة
- طبّقوا دوال Wither (withX) لإجراء تحديثات غير قابلة للتغيير
- تجاوزوا toString() لعرض البيانات للمستخدم بدلًا من مخرجات التصحيح
- تتوافق السجلات طبيعيًا مع كائنات القيمة في التصميم المُوجّه بالمجال
الأسئلة الشائعة
هل درس «الأساليب المخصصة في السجلّات» مجاني؟
نعم — نص درس «الأساليب المخصصة في السجلّات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «الأساليب المخصصة في السجلّات»؟
أضف أساليب الكائن وأساليب المصنع الثابتة لتعزيز وظائف السجلّات تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «الأساليب المخصصة في السجلّات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- مقدمة إلى السجلّات
- المنشئات المختصرة والتحقق
- الأساليب المخصصة في السجلّات
- السجلّات مقابل الفئات وLombok