تحويل قيم Optional
تحويل قيم Optional
في الدرس السابق تعلّمت كيف تستخرج قيمة من Optional بأمان باستخدام orElse وorElseGet وorElseThrow. لكن النمط الأكثر شيوعًا هو أنّك لا تريد استخراج القيمة على الإطلاق — بل تريد تطبيق منطق ما عليها والإبقاء على النتيجة مُغلَّفة، حتى تستمر حالة الغياب في الانتشار تلقائيًا. هذا بالضبط ما تفعله دوال map وflatMap وfilter.
Optional كأنّه Stream من عنصر واحد. تتيح لك دوال التحويل بناء خط معالجة متسلسل. إذا كان Optional فارغًا في أي خطوة، يُتخطّى باقي الخط تلقائيًا — دون فحوصات null، ودون جمل if.
map — تطبيق دالة على القيمة المُغلَّفة
map(Function<T, R> mapper) تُطبّق mapper إذا كانت قيمة موجودة وتُعيد Optional<R>. أما إذا كان الأصل فارغًا فتُعيد Optional.empty() دون استدعاء mapper أصلًا.
قارن ذلك بالنسخة الأمريّة (الإجرائية):
مع map تصف التحويل؛ أما انتشار حالة الغياب فيجري تلقائيًا.
flatMap — حين تُعيد الدالة قيمة Optional بحد ذاتها
لنفترض أنّ دالة التحويل تُعيد هي الأخرى Optional. لو استخدمت map العادية ستحصل على Optional<Optional<T>> متداخل، وهذا نادرًا ما تريده. flatMap تُسطّح هذا التداخل بمستوى واحد.
map حين تُعيد دالتك قيمة عادية. استخدم flatMap حين تُعيد دالتك Optional بالفعل. إذا رأيت Optional<Optional<...>> في IDE الخاص بك، الحل هو flatMap.
filter — إفراغ Optional بشرط
filter(Predicate<T> predicate) تُبقي على القيمة إذا أعادت الشرط true؛ وإلا تُعيد Optional.empty(). لن يُرمى أي استثناء — إذا كان Optional فارغًا أصلًا لا تُستدعى الدالة الشرطية.
بناء خط معالجة حقيقي
تتكامل الدوال الثلاث بسلاسة. إليك مثالًا واقعيًا: قراءة إعداد مستخدم من خريطة ضبط، وتحويله إلى عدد صحيح بعد التحقق منه، ثم تقييده ضمن نطاق مقبول.
لاحظ غياب أي if (x != null) في هذا الكود. يُعالج المسار الكامل للحالات الغائبة أو غير الصالحة بشكل هيكلي.
الفارق عن عمليات Stream
Optional.map تتصرف كـStream.map على تدفق من عنصر واحد على الأكثر. أحد الفوارق العملية: إذا أعادت دالة map الخاصة بك null، فإن Optional.map تحوّل النتيجة بصمت إلى Optional.empty() بدلًا من تغليف null. هذا متعمَّد — يمنع إعادة إدخال القيم الفارغة التي تحاول تجنّبها.
optional.map(x -> { doSomething(x); return x; }) لتشغيل أثر جانبي، استخدم ifPresent أو ifPresentOrElse بدلًا من ذلك. دوال التحويل تعبّر عن نية واضحة: "أنتج قيمة جديدة." تهريب آثار جانبية فيها يُربك القارئ وقد يُدخل أخطاء خفيّة.
or() — توفير Optional احتياطي (Java 9+)
أحيانًا تريد تجربة مصدر آخر حين يكون Optional الأول فارغًا، والمصدر الثاني هو الآخر Optional. or(Supplier<Optional<T>>) تُغطّي هذه الحالة بنظافة دون الحاجة إلى تحايلات flatMap:
هذا أكثر أمانًا من primary.orElseGet(() -> secondary.orElse(null))، الذي يستخرج القيمة مبكرًا ويفقد سياق Optional.
الخلاصة
استخدم map لتحويل قيمة موجودة والبقاء داخل Optional. استخدم flatMap حين يُعيد التحويل بحد ذاته Optional، لتجنّب التداخل. استخدم filter لاستبعاد القيم التي لا تُحقق شرطًا معينًا. سلسلها معًا لكتابة خطوط معالجة مقروءة وآمنة من القيم الفارغة. في الدرس القادم ستتعرف على الأنماط الشائعة التي تتألق فيها هذه الدوال — والأنماط المضادة التي يُساء فيها استخدامها.