كتاب "جافا الحديثة. تعبيرات Lambda ، والجداول ، والبرمجة الوظيفية "

صورة مرحبا ، habrozhiteli! تكمن ميزة التطبيقات الحديثة في الحلول المتقدمة ، بما في ذلك الخدمات المصغرة والبنى التفاعلية وتدفق البيانات. إن تعبيرات Lambda وتدفقات البيانات والنظام الذي طال انتظاره من الوحدات النمطية لنظام Java الأساسي يسهل تنفيذها إلى حد كبير.

سيساعدك الكتاب على تعلم الميزات الجديدة للوظائف الإضافية الحديثة ، مثل Stream API ونظام Java الأساسي. اكتشاف أساليب جديدة للقدرة التنافسية ومعرفة كيفية تحسين مفاهيم الوظيفة العمل مع التعليمات البرمجية.

في هذا الكتاب: • ميزات Java الجديدة • تدفق البيانات والبرمجة التفاعلية • نظام وحدة النظام الأساسي لـ Java.

مقتطفات. الفصل 11. فئة اختياري كأفضل بديل ل null


ارفع يدك إذا كنت قد حصلت على NullPointerException خلال حياتك المهنية كمطور جافا. اترك الأمر مرفوعًا إذا كان هذا الاستثناء هو الأكثر شيوعًا الذي واجهته. لسوء الحظ ، نحن لا نراكم الآن ، ولكن من المحتمل جدًا أن ترفع يدك. نشك أيضًا في أنك قد تفكر في شيء مثل: "نعم ، أوافق. الاستثناءات NullPointerException هي صداع لأي مطور جافا ، سواء كان مبتدئًا أو خبيرًا. ولكن لا يمكن القيام بأي شيء معهم على أي حال ، هذا هو الثمن الذي ندفعه لاستخدام هذا التصميم المريح وربما المحتوم كروابط فارغة. " هذا رأي مشترك في عالم البرمجة (الإلزامية) ؛ ومع ذلك ، ربما ليست هذه هي الحقيقة برمتها ، ولكنها تحيز متجذر بعمق.

اعترف اختصاصي علوم الكمبيوتر البريطاني توني هور ، الذي أنشأ روابط فارغة في عام 1965 ، أثناء تطوير لغة ALGOL W ، واحدة من لغات البرمجة المطبوعة الأولى مع السجلات التي تم تخصيص الذاكرة لها على الكومة ، في وقت لاحق أنه فعل هذا " فقط بسبب سهولة التنفيذ. " على الرغم من أنه أراد ضمان "السلامة الكاملة لاستخدام استثناء للروابط الفارغة ، فقد اعتقد أن هذه هي الطريقة الأكثر ملاءمة لمحاكاة عدم وجود قيمة. بعد عدة سنوات ، أعرب عن أسفه لهذا القرار ، واصفا إياه بأنه "خطأي مليار دولار". لقد رأينا جميعًا نتائج هذا القرار. على سبيل المثال ، يمكننا التحقق من حقل كائن لتحديد ما إذا كان يمثل إحدى قيمتين محتملتين ، فقط لإيجاد أننا نتحقق من عدم وجود كائن ، ولكن مؤشرًا خاليًا ، ونحصل على هذا NullPointerException المزعج على الفور.

في الواقع ، ربما يكون هوار قد قلل من التكلفة الباهظة لإصلاح ملايين مطوري الأخطاء التي تسببها الروابط الفارغة على مدار الخمسين عامًا الماضية. في الواقع ، تعتمد الغالبية العظمى من لغات البرمجة (1) التي تم إنشاؤها خلال العقود الماضية ، بما في ذلك Java ، على نفس قرار التصميم ، ربما لأسباب التوافق مع اللغات القديمة أو (على الأرجح) ، كما قال هوار ، "ببساطة بسبب سهولة التنفيذ ". نبدأ بعرض مثال بسيط للمشاكل التي صودفت عند استخدام null.

11.1. كيفية محاكاة نقص القيمة


تخيل أن لديك بنية الكائنات المتداخلة في القائمة 11.1 لمالك السيارة التي اشترت التأمين على السيارة.

القائمة 11.1. شخص / سيارة / نموذج بيانات التأمين

public class Person { private Car car; public Car getCar() { return car; } } public class Car { private Insurance insurance; public Insurance getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } } 

ما هو برأيك مشكلة في الكود التالي؟

 public String getCarInsuranceName(Person person) { return person.getCar().getInsurance().getName(); } 

يبدو هذا الرمز معقولًا ، لكن الكثير من الناس ليس لديهم سيارات ، فما هي نتيجة استدعاء طريقة getCar في هذه الحالة؟ في كثير من الأحيان (وبلا جدوى) يقومون بإرجاع رابط فارغ للإشارة إلى عدم وجود قيمة (في هذه الحالة ، للإشارة إلى عدم وجود جهاز). نتيجة لذلك ، يؤدي استدعاء طريقة getInsurance إلى إرجاع تأمين ارتباط فارغ ، مما يؤدي إلى طرح NullPointerException في وقت التشغيل وإيقاف البرنامج. لكن هذا ليس كل شيء. لكن ماذا لو كان جسم الشخص باطلاً؟ ماذا لو عاد getInsurance باطل؟

11.1.1. تقليل NullPointerException مع التحقق من الأمان


كيفية تجنب NullPointerException غير متوقع؟ يمكنك عادةً إضافة عمليات فحص خالية أينما تريد (وأحيانًا ، تتجاوز متطلبات البرمجة الآمنة ، وحيث لا تحتاج إلى ذلك) ، وغالبًا في أنماط مختلفة. تظهر محاولتنا الأولى لكتابة طريقة لمنع إنشاء NullPointerException في القائمة 11.2.

صورة

تقوم هذه الطريقة بالتحقق من وجود قيمة خالية في كل مرة يتم فيها إلغاء تحديد المتغير ، وإرجاع قيمة السلسلة "Unknown" إذا كان أحد المتغيرات التي تمت مواجهتها في سلسلة إلغاء التسجيل هذه على الأقل قيمة فارغة. الاستثناء الوحيد لهذه القاعدة هو أننا لا نتحقق من اسم شركة التأمين لأنها لاغية ، لأننا نعرف أنه (مثل أي شركة أخرى) يجب أن يكون لها اسم. يرجى ملاحظة أننا نجحنا في تجنب هذا الاختيار الأخير فقط بسبب معرفة مجال الموضوع ، ولكن هذه الحقيقة لا تنعكس في فئات Java التي تصمم بياناتنا.

لقد وصفنا الطريقة الواردة في القائمة 11.2 بأنها "شكوك عميقة" ، لأن نمط التكرار يكون ملحوظًا: في كل مرة تكون فيها شكوك ، يكون المتغير لاغياً ، يجب عليك إضافة كتلة متداخلة أخرى ، وبالتالي زيادة مستوى المسافة البادئة للرمز. من الواضح أن هذه التقنية لا تتوسع بشكل جيد وتقلل من إمكانية القراءة ، لذلك من الأفضل تجربة حل مختلف. لتجنب هذه المشكلة ، دعونا نتخذ مسارًا مختلفًا ، كما هو موضح في القائمة 11.3.

في المحاولة الثانية ، نحاول تجنب تداخل عميق إذا كانت الكتل تستخدم استراتيجية مختلفة: كلما تعثرنا في متغير فارغ ، نقوم بإرجاع قيمة السلسلة "Unknown". لكن هذا الحل أبعد ما يكون عن المثالية ؛ تحتوي الطريقة الآن على أربع نقاط خروج مختلفة ، مما يعقد عملية الصيانة إلى حد كبير. بالإضافة إلى ذلك ، يتم تكرار القيمة الافتراضية التي يتم إرجاعها في حالة null - السلسلة "Unknown" - في ثلاثة أماكن (ونأمل) ألا تحتوي على أخطاء إملائية! لتجنب ذلك ، يمكنك بالطبع نقل سلسلة التكرار إلى ثابت.

صورة

علاوة على ذلك ، هذه العملية عرضة للخطأ. ماذا لو نسيت التحقق مما إذا كانت إحدى الخصائص خالية؟ في هذا الفصل ، نجادل بأن استخدام لاغية لتمثيل نقص القيمة هو نهج خاطئ بشكل أساسي. مطلوب طريقة أفضل لنمذجة غياب القيمة وجودها.

11.1.2. المشاكل التي واجهتها مع فارغة


لتلخيص ما سبق ، يؤدي استخدام الروابط الفارغة في Java إلى المشكلات النظرية والعملية التالية.

  • بمثابة مصدر للأخطاء. NullPointerException هو الاستثناء الأكثر شيوعًا (بهامش واسع) في Java.
  • "ينفخ" رمز. تتفاقم قابلية القراءة بسبب الحاجة إلى ملء الكود مع عمليات التحقق الخالية ، والتي غالباً ما تكون متداخلة بعمق.
  • هذا لا معنى له. إنه يفتقر إلى أي معنى دلالي ، على وجه الخصوص ، مثل هذا النهج في صياغة غياب المعنى بلغة ذات تصنيف ثابت هو خطأ جوهري.
  • ينتهك أيديولوجية لغة جافا. تقوم Java دائمًا بإخفاء المؤشرات من المطورين ، مع استثناء واحد: مؤشر فارغ.
  • يخلق فجوة في نظام الكتابة. لا يتضمن null أي نوع أو معلومات أخرى ، لذلك يمكن تعيينه إلى أي نوع من الارتباط. قد يؤدي ذلك إلى حدوث مشاكل عند نقل قيمة خالية إلى جزء آخر من النظام حيث لا توجد معلومات حول ما يجب أن تكون عليه هذه القيمة في البداية.

كأساس للحلول الممكنة الأخرى في القسم الفرعي التالي ، ندرس بإيجاز الإمكانيات التي توفرها لغات البرمجة الأخرى.

11.1.3. بدائل للإلغاء في لغات البرمجة الأخرى


في السنوات الأخيرة ، تمكنت لغات مثل Groovy من التغلب على هذه المشكلة عن طريق إدخال مشغل اتصال آمن (؟) ، مصمم للعمل بأمان مع القيم الخالية المحتملة. لفهم كيفية تنفيذ هذه العملية في الممارسة العملية ، ضع في اعتبارك رمز Groovy التالي. تقوم باسترداد اسم شركة التأمين التي قام فيها الشخص المحدد بتأمين السيارة:

def carInsuranceName = person?.car?.insurance?.name

يجب أن يكون واضحًا لك ما يفعله هذا الرمز. قد لا يكون لدى شخص سيارة ، وللمحاكاة نلغيها في رابط السيارة الخاص بالكائن. وبالمثل ، قد تكون الآلة غير مؤمنة. يتيح لك مشغل المكالمات الآمن في Groovy العمل بأمان مع الروابط التي يحتمل أن تكون خالية دون رمي NullPointerException ، وتمرير الرابط الفارغ أسفل سلسلة الاتصال والعودة فارغة إذا كانت أي قيمة من السلسلة فارغة.

تم اقتراح ميزة مماثلة ليتم تنفيذها في Java 7 ، ولكن بعد ذلك تقرر عدم القيام بذلك. ومع ذلك ، من الغريب أن مشغل الاتصال الآمن ليس ضروريًا في Java. تتمثل الفكرة الأولى في أي مطور Java عند مواجهة NullPointerException في حل المشكلة بسرعة عن طريق إضافة عبارة if للتحقق من وجود null قبل استدعاء الأسلوب. لا يؤدي حل المشكلة بطريقة مماثلة ، دون النظر فيما إذا كانت null مقبولة في هذا الموقف بالذات بالنسبة للخوارزمية أو نموذج البيانات ، إلى تصحيح ، ولكن إلى إخفاء الخطأ. وبعد ذلك بالنسبة للمطور التالي (ربما نفسك خلال أسبوع أو شهر) ، سيكون من الصعب العثور على هذا الخطأ وإصلاحه. في الحقيقة ، أنت فقط تكتسح القمامة تحت السجادة. يعد مشغل إلغاء التسجيل الآمن في Groovy مجرد مكنسة أكبر وأقوى ، حيث يمكنك القيام بهذا الهراء دون القلق حقًا من العواقب.

تبحث لغات البرمجة الوظيفية الأخرى ، مثل Haskell و Scala ، في هذه المشكلة بشكل مختلف. لدى Haskell نوع ربما ، والذي يتضمن بشكل أساسي قيمة اختيارية. كائن من النوع ربما قد يحتوي على قيمة من النوع المحدد أو لا يحتوي على شيء. هاسكل يفتقر إلى مفهوم وجود صلة فارغة. في Scala ، لتضمين وجود أو عدم وجود قيمة من النوع T ، يتم توفير بنية منطقية مماثلة الخيار [T] ، والتي سنناقشها في الفصل 20. في هذه الحالة ، يجب عليك التحقق صراحةً من وجود قيمة باستخدام عمليات من النوع Option ، والتي توفر "اختبارات خالية" . الآن لم يعد من الممكن "نسيان التحقق من عدم وجود" ، لأن نظام الكتابة نفسه يتطلب التحقق.

حسنًا ، نحن خارج الموضوع قليلاً ، وكل هذا يبدو مجردة إلى حد كبير. من المحتمل أنك تتساءل عما يقدمه Java 8 بهذا المعنى ، فاستلهم من فكرة القيمة الاختيارية ، قدم منشئو Java 8 فئة جديدة ، java.util.Optional! في هذا الفصل ، نعرض ميزة استخدامه لنمذجة القيم التي قد تكون مفقودة بدلاً من تعيين مرجع فارغ لها. سنشرح أيضًا لماذا يتطلب هذا الانتقال من لاغٍ إلى اختياري أن يراجع المبرمج أيديولوجية العمل مع القيم الاختيارية في نموذج المجال. أخيرًا ، سوف نستكشف إمكانيات هذه الفئة الاختيارية الجديدة ونقدم بعض الأمثلة العملية لاستخدامها الفعال. نتيجة لذلك ، سوف تتعلم كيفية تصميم واجهات برمجة التطبيقات المحسنة ، والتي يفهم فيها المستخدم بالفعل من توقيع الطريقة ما إذا كانت هناك قيمة اختيارية ممكنة هنا.

11.2. تقديم الصف اختياري


في Java 8 ، وتحت تأثير لغتي Haskell و Scala ، ظهرت فئة java.util.Optional جديدة لتضمين قيمة اختيارية. على سبيل المثال ، إذا كنت تعرف أن شخصًا ما قد يكون أو لا يمتلك سيارة ، فيجب ألا تعلن عن السيارة المتغيرة في فئة الشخص بنوع السيارة وتعيينها كمرجع فارغ إذا لم يكن لدى الشخص سيارة ؛ بدلاً من ذلك ، يجب أن يكون نوعه اختياريًا ، كما هو موضح في الشكل. 11.1.

صورة

بالنظر إلى القيمة ، تعمل الفئة الاختيارية كمحول لها. على العكس من ذلك ، يتم تصميم غياب القيمة باستخدام الخيار الفارغ الذي يتم إرجاعه بواسطة الطريقة الاختيارية. تقوم طريقة المصنع الثابت هذه بإرجاع مثيل وحيد خاص للفئة الاختيارية. قد تتساءل عن الفرق بين رابط فارغ و اختياري. فارغ (). بمعنى أنه يمكن اعتبارهما متماثلين ، لكن في الممارسة العملية توجد اختلافات هائلة بينهما. تؤدي محاولة إلغاء التحديد الفارغ لا محالة إلى NullPointerException ، ويعد اختياري. (فارغ) عنصرًا صالحًا وعمليًا من النوع اختياري ، ويمكن الوصول إليه بسهولة. قريبا سترى كيف بالضبط.

هناك اختلاف مهم عملي في استخدام الكائنات الاختيارية بدلاً من فارغة: إعلان متغير من النوع اختياري يوضح بوضوح أنه يتم السماح بقيمة فارغة في هذه المرحلة. والعكس صحيح ، دائما باستخدام نوع السيارة ، وربما في بعض الأحيان تعيين مراجع فارغة لمتغيرات من هذا النوع ، يعني أنك تعتمد فقط على معرفة المجال الخاص بك لفهم ما إذا كان لاغ ينتمي إلى مجال التعريف لمتغير معين.

مع وضع ذلك في الاعتبار ، يمكنك إعادة النموذج الأصلي من القائمة 11.1. نستخدم الفئة الاختيارية ، كما هو موضح في القائمة 11.4.

لاحظ أن استخدام الفئة الاختيارية يثري دلالات النموذج. يعكس الحقل الاختياري في فئة الشخص والحقل الاختياري في فئة السيارة حقيقة أن شخصًا ما قد يكون أو لا يمتلك سيارة ، تمامًا كما يمكن أو لا يتم تأمين الجهاز.

في الوقت نفسه ، يشير إعلان شركة التأمين باسم String ، وليس اختياريًا ، إلى أن شركة التأمين يجب أن يكون لها اسم. وبالتالي ، أنت تعرف بالتأكيد أنك ستحصل على NullPointerException عند إلغاء تسجيل اسم شركة التأمين ؛ لا تعد إضافة التحقق الفارغ أمرًا ضروريًا ، لأنه سيؤدي فقط إلى إخفاء المشكلة. يجب أن يكون لشركة التأمين اسمًا ، لذلك إذا واجهت شركة بدون اسم ، فأنت بحاجة إلى معرفة ما هو الخطأ في البيانات ، وعدم إضافة رمز لإخفاء هذه الحقيقة.

صورة

ينشئ الاستخدام المتسق للقيم الاختيارية تمييزًا واضحًا بين القيمة التي قد تكون غائبة والقيمة المفقودة بسبب خطأ في الخوارزمية أو مشكلة في البيانات. من المهم ملاحظة أن الفئة الاختيارية لا تهدف إلى استبدال كافة الارتباطات الفارغة بروابط واحدة. وتتمثل مهمتها في المساعدة في تصميم واجهات برمجة تطبيقات أكثر قابلية للفهم ، بحيث من خلال توقيع الطريقة ، يمكن فهم ما إذا كان يمكن العثور على قيمة اختيارية هناك. يفرض نظام كتابة Java خيار فك الحزم لمعالجة عدم وجود قيمة.

عن المؤلفين


راؤول غابرييل أورما هو المدير التنفيذي والمؤسس المشارك لجامعة كامبريدج سبارك (المملكة المتحدة) ، وهي مجتمع تعليمي رائد لباحثي البيانات ومطوريها. تم اختيار راؤول كأحد المشاركين في برنامج أبطال جافا في عام 2017. وقد عمل في Google و eBay و Oracle و Goldman Sachs. دافع عن أطروحته في هندسة الكمبيوتر في جامعة كامبريدج. بالإضافة إلى ذلك ، حاصل على درجة الماجستير في الهندسة من جامعة إمبريال كوليدج في لندن ، وتخرج مع مرتبة الشرف وحصل على العديد من الجوائز لمقترحات الترشيد. قدم راؤول أكثر من 100 تقرير فني في المؤتمرات الدولية.

ماريو فوسكو هو كبير مهندسي البرمجيات في ريد هات ، ويشارك في تطوير Drools kernel ، محرك قاعدة JBoss. لديه خبرة غنية في تطوير جافا ، شارك (غالبًا كمطور رائد) في العديد من مشاريع الشركات في مختلف الصناعات ، من وسائل الإعلام إلى القطاع المالي. من بين اهتماماته البرمجة الوظيفية ولغات المجال المحددة. بناءً على هاتين الهويتين ، أنشأ مكتبة lambdaj مفتوحة المصدر ، يريد تطوير Java DSL داخلي للعمل مع المجموعات وتمكينه من استخدام بعض عناصر البرمجة الوظيفية في Java.

آلان ماكروفت أستاذ في قسم علوم الكمبيوتر بجامعة كامبريدج ، حيث يدرس منذ عام 1984. وهو أيضًا موظف في كلية روبنسون ، أحد مؤسسي الجمعية الأوروبية للغات وأنظمة البرمجة ، وأحد مؤسسي وأمناء مؤسسة Raspberry Pi Foundation. لديه شهادات في الرياضيات (كامبريدج) وعلوم الكمبيوتر (إدنبرة). آلان مؤلف أكثر من 100 مقالة علمية. وكان المشرف لأكثر من 20 مرشح دكتوراه. يتناول بحثه أساسًا مجال لغات البرمجة ودلالاتها والتحسين والتنفيذ. لفترة من الوقت ، عمل في AT&T Laboratories وقسم الأبحاث في شركة Intel ، وأيضًا شارك في تأسيس Codemist Ltd. ، التي أصدرت مترجم لغة C لهندسة ARM ، ودعا Norcroft.

»يمكن الاطلاع على مزيد من المعلومات حول الكتاب على موقع الناشر
» المحتويات
» مقتطفات

خصم 25٪ على كوبون للباعة المتجولين - جافا

عند دفع النسخة الورقية من الكتاب ، يتم إرسال كتاب إلكتروني عبر البريد الإلكتروني.

Source: https://habr.com/ru/post/ar470598/


All Articles