0Pricing
Java Academy · درس

تحويل Optional: ‏map وflatMap

سلسل التحويلات باستخدام map وflatMap لمعالجة القيم دون عمليات تحقق من null

تحويل Optional: ‏map وflatMap درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

تحويل Optionals: ‏map وflatMap

تحوّل map() القيمة داخل Optional إذا كانت موجودة. أما flatMap() فتتجنب Optionals المتداخلة عندما يعيد التحويل نفسه Optional.

map: تحويل القيمة

تطبّق map(Function) الدالة على القيمة إذا كانت موجودة، وتغلّف النتيجة في Optional جديد. وتعيد Optional فارغًا إذا كان فارغًا في الأصل.

import java.util.Optional;

Optional<String> name = Optional.of("alice");

Optional<String> upper = name.map(String::toUpperCase);
System.out.println(upper.get()); // ALICE

Optional<Integer> length = name.map(String::length);
System.out.println(length.get()); // 5

// Empty in → empty out
Optional<String> empty = Optional.<String>empty().map(String::toUpperCase);
System.out.println(empty.isPresent()); // false

تسلسل استدعاءات map

تتيح استدعاءات map المتعددة تسلسل التحويلات بسلاسة من دون فحوصات null متداخلة.

import java.util.Optional;

record Address(String city, String country) {}
record User(String name, Address address) {}

User user = new User("Alice", new Address("New York", "US"));

// Chain of maps — safe even if any step returns null
Optional<String> city = Optional.ofNullable(user)
    .map(User::address)
    .map(Address::city)
    .map(String::toUpperCase);

System.out.println(city.orElse("Unknown")); // NEW YORK

flatMap: تجنب Optional<Optional>

إذا كانت دالة التحويل تعيد Optional، فاستخدم flatMap للحفاظ على Optional مسطّح بدلًا من Optional<Optional> متداخل.

import java.util.Optional;

record User(String name, Optional<String> email) {}

User u = new User("Bob", Optional.of("bob@example.com"));

// map returns Optional<Optional<String>> — ugly!
Optional<Optional<String>> nested = Optional.of(u).map(User::email);

// flatMap flattens: Optional<String>
Optional<String> flat = Optional.of(u).flatMap(User::email);
System.out.println(flat.get()); // bob@example.com

تطبيق فعلي لـ flatMap: سلسلة repository

نمط شائع: تسلسل عمليات البحث في repository، حيث تعيد كل عملية Optional.

import java.util.Optional;

record Order(long id, long userId, double total) {}
record User(long id, String name, String email) {}

static Optional<Order> findOrder(long orderId) {
    return orderId == 1 ? Optional.of(new Order(1, 42, 99.99)) : Optional.empty();
}
static Optional<User> findUser(long userId) {
    return userId == 42 ? Optional.of(new User(42, "Alice", "a@co.com")) : Optional.empty();
}

Optional<String> email = findOrder(1)
    .flatMap(o -> findUser(o.userId()))
    .map(User::email);

System.out.println(email.orElse("Not found")); // a@co.com

ملخص map مقابل flatMap

اختيار ما بين map وflatMap:

  • استخدم map عندما تعيد الدالة قيمة عادية (String أو Integer)
  • استخدم flatMap عندما تعيد الدالة Optional
import java.util.Optional;

// map: T → R
Optional.of("hello").map(String::toUpperCase);       // Optional<String>
Optional.of("42").map(Integer::parseInt);             // Optional<Integer>

// flatMap: T → Optional<R>
Optional.of("42").flatMap(s -> {
    try { return Optional.of(Integer.parseInt(s)); }
    catch (NumberFormatException e) { return Optional.empty(); }
}); // Optional<Integer> (not Optional<Optional<Integer>>)

تحليل آمن باستخدام flatMap

نمط مفيد: تغليف العمليات الخطرة التي قد تفشل داخل Optional باستخدام flatMap.

import java.util.Optional;

static Optional<Integer> tryParseInt(String s) {
    try { return Optional.of(Integer.parseInt(s)); }
    catch (NumberFormatException e) { return Optional.empty(); }
}

Optional<String> input = Optional.of("123");
Optional<Integer> parsed = input.flatMap(JavaOptional::tryParseInt);
System.out.println(parsed.map(n -> n * 2).orElse(-1)); // 246

Optional<String> bad = Optional.of("abc");
Optional<Integer> failed = bad.flatMap(JavaOptional::tryParseInt);
System.out.println(failed.orElse(-1)); // -1

جمع Optionals

صفِّ stream من Optionals وحوّله لجمع القيم الموجودة فقط.

import java.util.*;
import java.util.stream.*;

List<String> inputs = List.of("10", "abc", "25", "nope", "7");

List<Integer> numbers = inputs.stream()
    .flatMap(s -> {
        try { return Stream.of(Integer.parseInt(s)); }
        catch (NumberFormatException e) { return Stream.empty(); }
    })
    .collect(Collectors.toList());

System.out.println(numbers); // [10, 25, 7]

التنقل بين الكائنات المتداخلة

يجعل map وflatMap التنقل الآمن في مخطط الكائنات العميق ممكنًا من دون فحوصات null المملة.

import java.util.Optional;

record Company(String name, Optional<Address> address) {}
record Address(String city, Optional<String> zipCode) {}

Company company = new Company(
    "Acme Corp",
    Optional.of(new Address("Austin", Optional.of("78701")))
);

String zip = Optional.of(company)
    .flatMap(Company::address)
    .flatMap(Address::zipCode)
    .orElse("N/A");

System.out.println(zip); // 78701

map لتحويل الأنواع

يُعد map مناسبًا جدًا لتحويل أنواع قيم Optional، مثل التحليل أو التنسيق أو استخراج حقل فرعي.

import java.util.Optional;
import java.time.LocalDate;

// Parse a date from String safely
static Optional<LocalDate> parseDate(String s) {
    try { return Optional.of(LocalDate.parse(s)); }
    catch (Exception e) { return Optional.empty(); }
}

Optional<String> dateStr = Optional.of("2024-06-15");
Optional<Integer> year = dateStr
    .flatMap(JavaOptional::parseDate)
    .map(LocalDate::getYear);

System.out.println(year.orElse(-1)); // 2024

بناء مسارات المعالجة

يساعد دمج filter وmap وflatMap على بناء مسارات معالجة بيانات واضحة تتعامل مع الغياب في كل خطوة من دون فحوصات null صريحة.

import java.util.Optional;

record Product(String sku, String category, boolean inStock, double price) {}

static Optional<Product> findProduct(String sku) {
    if ("LAPTOP-1".equals(sku))
        return Optional.of(new Product("LAPTOP-1", "Electronics", true, 999.99));
    return Optional.empty();
}

Optional<String> priceTag = findProduct("LAPTOP-1")
    .filter(Product::inStock)
    .filter(p -> p.price() < 2000)
    .map(p -> String.format("%s: $%.2f", p.sku(), p.price()));

System.out.println(priceTag.orElse("Not available")); // LAPTOP-1: $999.99

تحقق سريع

متى ينبغي استخدام flatMap بدلًا من map مع Optional؟

مراجعة: تحويل Optionals: ‏map وflatMap

أهم النقاط:

  • تحوّل map() القيمة إذا كانت موجودة، وتغلّف النتيجة في Optional
  • تُستخدم flatMap() مع الدوال التي تعيد Optional، فتتجنب Optionals المتداخلة
  • سلسل استدعاءات map المتعددة للتحويلات متعددة الخطوات من دون فحوصات null
  • استخدم flatMap لتسلسل استدعاءات repository أو service التي تعيد Optional
  • ادمج filter وmap وflatMap لبناء مسارات معالجة بيانات آمنة من null
  • استخدم flatMap مع Optional::stream لدمج Optionals في مسارات معالجة streams

الأسئلة الشائعة

هل درس «تحويل Optional: ‏map وflatMap» مجاني؟

نعم — نص درس «تحويل Optional: ‏map وflatMap» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «تحويل Optional: ‏map وflatMap»؟

سلسل التحويلات باستخدام map وflatMap لمعالجة القيم دون عمليات تحقق من null تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «تحويل Optional: ‏map وflatMap»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. سبب وجود Optional
  2. إنشاء Optional وفحصه
  3. تحويل Optional: ‏map وflatMap
  4. أفضل ممارسات Optional
← العودة إلى Java Academy