تحويل 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 YORKflatMap: تجنب 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); // 78701map لتحويل الأنواع
يُعد 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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- سبب وجود Optional
- إنشاء Optional وفحصه
- تحويل Optional: map وflatMap
- أفضل ممارسات Optional