سبب وجود Optional
افهم مشكلة null وكيفية استخدام Optional لتمثيل غياب قيمة بأمان
سبب وجود Optional درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
سبب وجود Optional
يمثل الصنف Optional احتمال غياب قيمة. وهو يجبر المستدعين على معالجة حالة «عدم وجود قيمة» صراحةً، مما يقلل من NullPointerExceptions.
مشكلة null
إن إرجاع null من الدوال للدلالة على «عدم وجود قيمة» أمر خطير؛ فقد ينسى المستدعون التحقق منه، مما يؤدي إلى NPEs في موضع عميق من مكدس الاستدعاء.
// Old way — null return invites NPE
static String findEmail(int userId) {
if (userId == 1) return "alice@example.com";
return null; // caller might forget to check!
}
String email = findEmail(99);
System.out.println(email.toUpperCase()); // NullPointerException!Optional كنوع إرجاع
يجعل إرجاع Optional<T> احتمال غياب القيمة واضحًا في توقيع الدالة، إذ يجب على المستدعين معالجته.
import java.util.Optional;
static Optional<String> findEmail(int userId) {
if (userId == 1) return Optional.of("alice@example.com");
return Optional.empty();
}
// Caller must handle the Optional
Optional<String> result = findEmail(99);
if (result.isPresent()) {
System.out.println(result.get().toUpperCase());
} else {
System.out.println("User not found");
}Optional ليس حلًا سحريًا
لا تستخدم Optional كنوع لحقل أو وسيط دالة أو عنصرًا داخل مجموعات. وحالة الاستخدام الأساسية له هي نوع إرجاع من الدوال التي قد لا تعيد قيمة.
// WRONG: Optional as field
class User {
private Optional<String> nickname; // bad practice
}
// WRONG: Optional as parameter
void save(Optional<String> name) {} // just use overloading
// CORRECT: Optional only as return type
static Optional<User> findById(long id) {
// returns Optional.empty() if not found
return database.findById(id);
}Optional مقابل الاستثناء
استخدم Optional للغياب المتوقع (مثل عدم العثور على مستخدم أو فراغ الإعدادات). واستخدم الاستثناءات لحالات الفشل غير المتوقعة (مثل خطأ اتصال قاعدة البيانات أو تنسيق غير صالح).
// Good use of Optional: user might not exist
static Optional<User> findUser(String email) { ... }
// Good use of exception: invalid operation
static User createUser(String email) {
if (!email.contains("@"))
throw new IllegalArgumentException("Invalid email");
// ...
}
// Rule of thumb:
// Optional = absence is a normal, expected outcome
// Exception = something went wrong that should not happenOptional داخل المجموعات
لا تضع Optional داخل مجموعة أبدًا. بدلًا من ذلك، صفِّ القيم الغائبة باستخدام streams.
import java.util.*;
import java.util.stream.*;
List<String> ids = List.of("user:1", "cache:miss", "user:2");
// BAD: List<Optional<User>>
// GOOD: filter optionals out of streams
List<String> users = ids.stream()
.map(id -> id.startsWith("user:") ? Optional.of(id) : Optional.<String>empty())
.flatMap(Optional::stream) // Java 9+: flatMap empty optionals
.collect(Collectors.toList());
System.out.println(users); // [user:1, user:2]التصميم باستخدام Optional
يجعل التصميم المدرك لـ Optional واجهات API أكثر وضوحًا بشأن سلوك الإرجاع. إليك نمط repository باستخدام Optional.
import java.util.*;
interface UserRepository {
Optional<User> findById(long id);
Optional<User> findByEmail(String email);
List<User> findAll(); // no Optional — returns empty list if none
}
record User(long id, String name, String email) {}
// The caller sees immediately that the result might be absent
UserRepository repo = /* inject */;
repo.findByEmail("alice@example.com")
.ifPresentOrElse(
u -> System.out.println("Found: " + u.name()),
() -> System.out.println("Not found")
);Optional وStreams
يتكامل Optional جيدًا مع Streams عبر stream() (في Java 9)، الذي ينتج stream يحتوي على صفر أو عنصر واحد.
import java.util.*;
import java.util.stream.*;
List<Optional<String>> maybeNames = List.of(
Optional.of("Alice"),
Optional.empty(),
Optional.of("Bob"),
Optional.empty(),
Optional.of("Charlie")
);
List<String> present = maybeNames.stream()
.flatMap(Optional::stream) // Java 9+
.collect(Collectors.toList());
System.out.println(present); // [Alice, Bob, Charlie]Optional.of مقابل ofNullable
ترمي Optional.of(value) استثناء NPE إذا كانت value تساوي null. بينما تغلّف Optional.ofNullable(value) القيمة null بأمان باعتبارها Optional فارغًا.
import java.util.Optional;
String name = null;
// Optional.of(null) throws NullPointerException!
try {
Optional<String> bad = Optional.of(name);
} catch (NullPointerException e) {
System.out.println("NPE from Optional.of(null)");
}
// Optional.ofNullable(null) returns empty safely
Optional<String> safe = Optional.ofNullable(name);
System.out.println(safe.isPresent()); // false
System.out.println(safe.isEmpty()); // true (Java 11+)ملاحظة حول الأداء
يغلّف Optional القيمة داخل كائن، مما يضيف تكلفة صغيرة لتخصيص الذاكرة لكل Optional. وفي المسارات الساخنة التي تنشئ ملايين الكائنات في الثانية، فكّر في استخدام فحوصات null بدلًا منه. أما في شيفرة الخدمات المعتادة، فـ Optional خيار أفضل من حيث سهولة القراءة.
// In hot loops (millions/second), null check might be preferred:
// String result = findFastPath(id); // returns null or value
// if (result != null) use(result);
// For typical service/repository code:
// Optional<String> result = findValue(id);
// result.ifPresent(v -> use(v));
// Modern JVMs are good at escape analysis — many Optional allocations
// are stack-allocated and cause no heap pressure.السياق التاريخي: خطأ توني هور الفادح الذي كلّف مليارات الدولارات
وصف توني هور، الذي ابتكر مراجع null عام 1965، ذلك بأنه «خطؤه الفادح الذي كلّف مليارات الدولارات»، إذ أدى إلى عدد لا يحصى من أعطال الأنظمة. وOptional هو إجابة Java لجعل الغياب صريحًا وآمنًا.
// Java's Optional was inspired by functional languages:
// Haskell's Maybe, Scala's Option, Rust's Option<T>
// The goal: make the type system express that absence is possible
// Instead of relying on documentation or developer vigilance
// Before Optional:
// String email = user.getEmail(); // is this null? Check the docs!
// With Optional:
// Optional<String> email = user.getEmail(); // type says: might be absentتحقق سريع
متى ينبغي استخدام Optional كنوع إرجاع؟
مراجعة: سبب وجود Optional
أهم النقاط:
- يمثل Optional قيم الإرجاع الاختيارية، مما يجعل الغياب واضحًا في النوع
- استخدم Optional.of() عندما لا تكون القيمة null أبدًا، وofNullable() عندما يمكن أن تكون كذلك
- استخدم Optional.empty() لإرجاع حالة الغياب
- لا تستخدم Optional كحقل أو وسيط دالة أو داخل المجموعات أبدًا
- يتكامل Optional مع streams عبر stream() (في Java 9 وما بعدها)
- اختر Optional بدلًا من إرجاع null لتحسين تصميم API، واختر الاستثناءات لمعالجة حالات الفشل
الأسئلة الشائعة
هل درس «سبب وجود Optional» مجاني؟
نعم — نص درس «سبب وجود Optional» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «سبب وجود Optional»؟
افهم مشكلة null وكيفية استخدام Optional لتمثيل غياب قيمة بأمان تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «سبب وجود Optional»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.