أفضل ممارسات Optional
طبّق orElse وorElseGet وorElseThrow وتجنب الأنماط الشائعة غير السليمة في Optional
أفضل ممارسات Optional درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
أفضل ممارسات Optional
يُعد Optional أداة قوية عند استخدامه بطريقة صحيحة. يغطي هذا الدرس ما ينبغي فعله وما ينبغي تجنبه، مع أمثلة عملية من قواعد شيفرة واقعية.
افعل: استخدم Optional كنوع إرجاع
حالة الاستخدام الأساسية لـ Optional هي أن يكون نوع إرجاع من الدوال التي قد لا تعثر على قيمة أو تنتجها.
import java.util.Optional;
// Good: signals to caller that result might be absent
public Optional<User> findByUsername(String username) { ... }
public Optional<Config> getConfig(String key) { ... }
public Optional<String> getHeader(String name) { ... }
// These are all cases where absence is a normal outcome,
// not an exceptional conditionلا تفعل: استخدام Optional كحقل
تضيف حقول Optional عبئًا على الكائن وتعقّد عملية التسلسل. استخدم null أو قيمة مميزة للحقول الاختيارية داخل الأصناف.
// BAD
class User {
private Optional<String> middleName; // wrong
}
// GOOD: use null for absent optional field
class User {
private final String middleName; // null = absent
public Optional<String> middleName() {
return Optional.ofNullable(middleName); // expose as Optional in getter
}
}
User u = new User(null); // null field
System.out.println(u.middleName().isPresent()); // falseلا تفعل: استخدام Optional كوسيط دالة
تجبر الوسائط من نوع Optional المستدعين على تغليف القيم في Optional دون داعٍ. استخدم التحميل الزائد أو @Nullable بدلًا من ذلك.
// BAD
void sendNotification(String message, Optional<String> subject) { ... }
// caller must write: sendNotification("Hello", Optional.of("News"))
// GOOD: overloading
void sendNotification(String message) { sendNotification(message, null); }
void sendNotification(String message, String subject) {
// subject may be null
}
// caller writes: sendNotification("Hello", "News") — cleanلا تفعل: استخدام Optional داخل المجموعات
لا تضع Optionals داخل List أو Set أو قيمة Map أبدًا. لا معنى لـ Optionals الفارغة داخل المجموعات، لذا احذف العنصر ببساطة.
// BAD: List of Optionals
List<Optional<User>> results = ...; // meaningless empty Optionals
// GOOD: List of present values only
List<User> presentUsers = maybeUsers.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
// BAD: Map with Optional values
Map<String, Optional<String>> config = ...;
// GOOD: just use null or omit the key for absent values
Map<String, String> config2 = ...;
String value = config2.getOrDefault("key", "default");افعل: استخدم orElseGet للقيم الافتراضية المكلفة
عندما تتضمن القيمة الافتراضية عملية حسابية (استعلام قاعدة بيانات أو استدعاء شبكة)، استخدم orElseGet دائمًا؛ إذ لا يُستدعى المورّد إلا عند الحاجة.
import java.util.Optional;
// BAD: buildDefault() always called, even when value is present!
String config = getConfig("timeout").orElse(buildDefault());
// GOOD: supplier called lazily only when empty
String config2 = getConfig("timeout").orElseGet(() -> buildDefault());
// Or with method reference:
String config3 = getConfig("timeout").orElseGet(this::buildDefault);
// For cheap literals, orElse is fine
String name = getConfig("name").orElse("default");افعل: استخدم orElseThrow للقيم المطلوبة
عندما تكون القيمة المفقودة خطأً برمجيًا أو انتهاكًا لثابت، استخدم orElseThrow مع استثناء وصفي.
import java.util.Optional;
// Good: throw when absence is an error
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException("User not found: " + userId));
// Good: in validation code
String apiKey = Optional.ofNullable(System.getenv("API_KEY"))
.orElseThrow(() -> new IllegalStateException(
"API_KEY environment variable is required"));
System.out.println("Key found: " + apiKey.length() + " chars");نمط مضاد: isPresent + get()
تجنب النمط المضاد المتمثل في فحص isPresent() ثم استدعاء get(). فهذا ليس أفضل من فحص null ويُفقد Optional الغرض منه.
import java.util.Optional;
Optional<String> opt = findValue();
// BAD: isPresent() + get() — no improvement over null check
if (opt.isPresent()) {
String val = opt.get();
process(val);
}
// GOOD: use the functional API
opt.ifPresent(this::process);
// GOOD: transform and return
return opt.map(this::process).orElse(null);
// GOOD: provide a default
String result = opt.orElse("default");أفضل ممارسة لتسلسل العمليات
أنشئ سلسلة واضحة: بحث ← تصفية ← تحويل ← قيمة بديلة. توضح كل خطوة الغرض منها ذاتيًا.
import java.util.Optional;
record User(long id, String plan, boolean active) {}
static Optional<User> findUser(long id) { return Optional.empty(); }
String result = findUser(42L)
.filter(User::active) // keep only active users
.filter(u -> "PRO".equals(u.plan())) // keep only PRO users
.map(u -> "Welcome, Pro user #" + u.id()) // transform
.orElse("Upgrade to Pro!"); // fallback
System.out.println(result); // Upgrade to Pro!Optional في طبقة الخدمة
مثال واقعي لطبقة خدمة يتبع أفضل الممارسات.
import java.util.Optional;
class OrderService {
public Optional<OrderSummary> getOrderSummary(long orderId) {
return orderRepo.findById(orderId)
.filter(o -> !o.isCancelled())
.map(this::toSummary);
}
public OrderSummary requireOrderSummary(long orderId) {
return getOrderSummary(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
}
private OrderSummary toSummary(Order o) { return new OrderSummary(o); }
}
// API callers choose: Optional (handle absence) or require (exception)Optional وأدوات أمان null
يكمل Optional التعليقات التوضيحية @Nullable و@NonNull وأدوات مثل SpotBugs وNullAway لتوفير تغطية شاملة لأمان null.
// Annotation-based: declares intent for in-class nulls
import org.jetbrains.annotations.*;
class ProductService {
@Nullable // may return null — for framework/bean use
public String getCachedName(String id) { return cache.get(id); }
// Optional — for service API that handles absence
public Optional<Product> findProduct(String id) {
return repo.findById(id);
}
}
// Use @Nullable for fields/parameters; Optional for return typesتحقق سريع
ما الخطأ في الشيفرة التالية؟
if (opt.isPresent()) {
process(opt.get());
}مراجعة: أفضل ممارسات Optional
أهم النقاط:
- استخدم Optional كنوع إرجاع فقط، وليس كحقل أو وسيط دالة أبدًا
- فضّل orElseGet على orElse عندما تكون عملية حساب القيمة الافتراضية مكلفة
- استخدم orElseThrow عندما يمثل الغياب خطأً برمجيًا
- تجنب isPresent() + get()، واستخدم ifPresent أو map أو orElse بدلًا من ذلك
- لا تضع Optional داخل المجموعات أبدًا، بل صفِّ streams بدلًا من ذلك
- أنشئ سلاسل سهلة القراءة: filter ← map ← orElse لتوضيح تدفق البيانات
الأسئلة الشائعة
هل درس «أفضل ممارسات Optional» مجاني؟
نعم — نص درس «أفضل ممارسات Optional» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «أفضل ممارسات Optional»؟
طبّق orElse وorElseGet وorElseThrow وتجنب الأنماط الشائعة غير السليمة في Optional تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «أفضل ممارسات Optional»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- سبب وجود Optional
- إنشاء Optional وفحصه
- تحويل Optional: map وflatMap
- أفضل ممارسات Optional