مقدّمة إلى Optional
مقدّمة إلى Optional
في الدرس السابق رأينا كيف يُفضي null إلى NullPointerException وعقود غير واضحة وكود دفاعي منتشر في كل مكان. قدّم Java 8 الفئة java.util.Optional<T> كحاوية مكتوبة تعبّر صراحةً عن احتمالية الغياب. يتناول هذا الدرس الطرق الثلاث لإنشاء Optional والأساليب الأساسية للتحقّق من وجود قيمة بداخله.
ما هو Optional وما ليس عليه
Optional<T> غلاف من نوع قيمة. له حالتان فحسب:
- حاضر — يحتوي على قيمة غير null من النوع
T. - فارغ — لا يحتوي على أي قيمة (مشابه لـ
nullلكنه صريح).
الطرق الثلاث للإنشاء
Optional.of — حين تكون متأكدًا من وجود القيمة
Optional.of(value) يُغلّف قيمة تعلم يقينًا أنها غير null. إذا مرّرت null رمى NullPointerException فورًا، وهذا مقصود — يفشل مبكرًا عند موقع الإنشاء بدلًا من الانتشار الصامت.
استخدم Optional.of حين تأتي القيمة من مصدر تتحكّم فيه وتضمن أنها غير null — كثابت أو كائن منشأ للتو أو قيمة تحقّقت منها للتو.
Optional.ofNullable — حين قد تكون القيمة null أو لا
Optional.ofNullable(value) هو أكثر المصانع شيوعًا. يفحص القيمة: إن كانت غير null يُعيد Optional حاضرًا، وإن كانت null يُعيد Optional فارغًا. إنه الجسر بين واجهات برمجية قديمة تُعيد null وعالم Optional.
Optional.empty — Optional فارغ صريح
Optional.empty() ينشئ حاوية فارغة بلا قيمة. تستخدمه في الدوال التي تُعيد Optional<T> حين لا يوجد شيء لإعادته — إنه البديل المكتوب والمتعمَّد لعبارة return null.
إعادة Optional.empty() أكثر صدقًا بكثير من إعادة null: يُلزم المُترجم المُستدعِين بالتعامل مع نوع الإعادة Optional<String>، ولا حاجة لأي تعليق أو تعليقة توضيحية للتعبير عن الغياب.
التحقّق من وجود القيمة
بعد الحصول على Optional يمكنك الاستفسار عما إذا كان يحتوي على قيمة بثلاث طرق مترابطة:
isPresent()— تُعيدtrueإذا كانت قيمة موجودة.isEmpty()— تُعيدtrueإذا كانت الحاوية فارغة (أُضيفت في Java 11؛ عكسisPresent()بصياغة أوضح).get()— تُعيد القيمة المُغلّفة، لكنها ترميNoSuchElementExceptionإذا كانت الحاوية فارغة.
get() المجرّد في كود الإنتاج. استدعاء get() دون فحص isPresent() مسبق خطير تمامًا مثل إلغاء مرجع قد يكون null — لقد استبدلت NullPointerException بـ NoSuchElementException فحسب. في الدروس القادمة ستتعلّم طرق الاستخراج الأكثر أمانًا (orElse وorElseGet وmap وifPresent) التي تعبّر عن النية بوضوح ولا يمكنها الرمي.
المساواة وتمثيل النص
تتجاوز Optional دالتي equals وhashCode بتفويض الأمر للقيمة المُغلّفة؛ فـ Optional حاضران تحويان قيمتين متساويتين يعتبران متساويين، وOptional فارغان يعتبران متساويين دائمًا.
اختيار الطريقة المناسبة للإنشاء
شجرة قرار بسيطة:
- تتحكّم في القيمة وهي لن تكون null أبدًا →
Optional.of(value) - القيمة تأتي من واجهة برمجية قديمة أو مدخلات مستخدم قد تكون null →
Optional.ofNullable(value) - تكتب دالة وليس لديك نتيجة لإعادتها →
Optional.empty()
ofNullable عند الشك. إذا لم تكن متأكدًا 100% من أن القيمة غير null استخدم ofNullable. التحقّق الإضافي من null لا يُكلّف شيئًا يُذكر، والفشل المبكّر لـ of مفيد فقط حين تريد فعلًا الانهيار الصاخب عند انتهاك العقد — كالتحقّق في المنشئ مثلًا.
الخلاصة
Optional.of يُغلّف قيمة مضمونة غير null ويفشل مبكّرًا عند null. Optional.ofNullable يُغلّف أي شيء محوّلًا null إلى فارغ بصمت. Optional.empty() يمثّل إعادة لا-قيمة صريحة. للتفتيش عن الحالة استخدم isPresent() أو isEmpty() (Java 11+)، وعامِل get() المجرّد باعتباره ملاذًا أخيرًا يستلزم حمايةً. في الدرس القادم ستتعرّف على واجهة الاستخراج والتحويل الأغنى — orElse وorElseGet وorElseThrow — التي تجعل Optional مفيدًا حقًا لا مجرّد فحص null مُعاد تسميته.