ملخص
عندما كنت أحرارًا ، كنت أستغرق وقتًا لتعلم Groovy و Scala على تشغيل JVM ، ووجدت أنهم يتعاملون مع فارغ أكثر من الإصدارات السابقة من Java. في Java 8 ، يقدم اختياري حلاً أنيقًا للغاية للمعالجة الفارغة للبرمجة الوظيفية. ستشرح هذه المقالة المعالجة السيئة التي طال أمدها في Java ، ثم تقديم استخدام اختياري لتنفيذ البرمجة الوظيفية Java.
الفارغ الذي أزعجنا في تلك السنوات
هناك أسطورة في عالم Java: فقط عندما تفهم حقًا استثناء المؤشر الفارغ ، هل يمكن اعتبار مطور Java مؤهل. في حياتنا المهنية في جافا ، سنواجه العديد من المعالجة الفارغة كل يوم. قد نكتب رموزًا مثل ما يلي مرارًا وتكرارًا كل يوم:
if (null! = obj1) {if (null! = obje2) {// افعل شيئًا}}إذا كان لديك القليل من الرؤية ، فستقوم Javaer ببعض الأشياء الجميلة وإيجاد طريقة للحكم على Null:
Boolean CheckNotnull (Object obj) {return null == obj؟ خطأ: صحيح } void do () {if (checknotnull (obj1)) {if (checknotnull (obj2)) {// افعل شيئًا}}}ثم ، يأتي السؤال مرة أخرى: إذا كان الفارغ يمثل سلسلة فارغة ، فماذا يعني ""؟
ثم يخبرنا التفكير بالقصور الذاتي ، "" "و Null كلا رموز السلسلة الفارغة؟ لقد قمت ببساطة بترقية القيمة الخالية:
Boolean CheckNotblank (Object obj) {return null! = obj &&! "". يساوي (obj)؟ صحيح: خطأ } void do () {if (checkNotBlank (obj1)) {if (checkNotnull (obj2)) {// افعل شيئًا}}}}إذا كان لديك وقت ، فيمكنك التحقق من مقدار الرمز الذي كتبته في المشروع الحالي أو الرمز السابق الخاص بك وما هو مشابه للرمز أعلاه.
أتساءل عما إذا كنت قد فكرت بجدية في سؤال: ماذا يعني فارغ؟
متذكر ، كم مرة واجهنا java.lang.nullpointerxception استثناءات في حياتنا المهنية السابقة؟ NullPointerException هو استثناء مستوى RunTimeException لا يلزم التقاطه. إذا تعاملنا معها عن طريق الخطأ ، فغالبًا ما نرى مخرجات مكدس استثناءات مختلفة ناتجة عن NullPointerException في سجل الإنتاج. وبناءً على معلومات مكدس الاستثناء هذه ، لا يمكننا تحديد سبب المشكلة على الإطلاق ، لأنه ليس المكان الذي تم فيه إلقاء NullPointerxception ، مما تسبب في المشكلة. علينا أن نتعمق أكثر لمعرفة أين تم إنشاء هذا فارغ ، وغالبًا ما لا يمكن تعقب السجلات في هذا الوقت.
في بعض الأحيان يكون من المأساوي أكثر مأساوية أن الأماكن التي يتم إنتاج القيم الخالية لا تكون غالبًا في رمز المشروع الخاص بنا. هذا له حقيقة أكثر إحراجًا - عندما نسمي العديد من واجهات الطرف الثالث من نوعية مختلفة ، من الصعب معرفة ما إذا كانت الواجهة تُرجع خالية من الصدفة ...
العودة إلى المشكلة المعرفية السابقة من NULL. يعتقد العديد من الجافاي أن فارغ لا يعني "لا شيء" أو "القيمة غير موجودة". وفقًا لهذا التفكير بالقصور الذاتي ، فإن منطق الكود الخاص بنا هو: يمكنك الاتصال بالواجهة الخاصة بي وإرجاع "القيمة" المقابلة وفقًا للمعلمات التي تعطيني. إذا لم يتمكن هذا الشرط من العثور على "القيمة" المقابلة ، فبالطبع سأعيد لك خالية من عدم وجود "شيء". دعونا نلقي نظرة على الكود التالي ، الذي يتم كتابته بأسلوب ترميز Java تقليديًا وعاديًا للغاية:
فئة myentity {int id ؛ اسم السلسلة String getName () {return name ؛ }} // اختبار الطبقة الرئيسية {public static void main (string [] args) final myentity myentity = getMyentity (false) ؛ System.out.println (myentity.getName ()) ؛ } private getMyentity (Boolean ISSUC) {if (ISSUC) {return new myentity () ؛ } آخر {return null ؛ }}}يعد هذا الرمز بسيطًا للغاية ، كما أن رمز الأعمال اليومي أكثر تعقيدًا من هذا ، ولكن في الواقع ، يتم كتابة عدد كبير من رموز Java وفقًا لهذا الروتين. يمكن للأشخاص الذين يعرفون كيفية استخدام البضائع أن يروا في لمحة أنهم سوف يرمون بالتأكيد nullpointerxception في النهاية. ومع ذلك ، عندما نكتب رمز العمل ، نادراً ما نفكر في التعامل مع هذا الخالية المحتملة (ربما تمت كتابة وثيقة واجهة برمجة التطبيقات بوضوح شديد وستعود في بعض الحالات ، ولكن هل تتأكد من أنك ستقرأ بعناية وثيقة API قبل البدء في كتابة الرمز؟) ، حتى وصلنا إلى مرحلة معينة من الاختبار ، قد تكون هناك مجموعة من المبلغين.
// Mainpublic Class Test {public static void main (string [] args) final myentity myentity = getMyentity (false) ؛ if (null! = myentity) {system.out.println (myentity.getName ()) ؛ } آخر {system.out.println ("error") ؛ }}}فكر بعناية في السنوات القليلة الماضية ، هل فعلنا جميعًا هذا؟ إذا كان لا يمكن اكتشاف بعض المشكلات الخالية حتى مرحلة الاختبار ، فستكون المشكلة الآن - كم عدد الفهود لا تتم معالجتها بشكل صحيح في هذه الرموز التجارية المعقدة والمنظمة؟
غالبًا ما يمكن رؤية نضج ودقة المشروع عند التعامل مع NULL. على سبيل المثال ، أعطت الجوافة طريقة معالجة خالية أنيقة قبل JDK1.6 ، والتي توضح مدى عمق المهارات.
خالية شبحية تمنعنا من التقدم
إذا كنت من Javaer تركز على التطوير التقليدي الموجهة للكائنات ، فيمكنك استخدامك للمشاكل المختلفة التي تسببها Null. ولكن منذ سنوات عديدة ، قال الله العظيم أن Null هو حفرة.
قال توني هول (لا تعرف من هو هذا الرجل؟ اذهب للتحقق من ذلك بنفسك) قال ذات مرة: "أسميها خطأي بمليارات الدولارات. لقد كان اختراع المرجع الفارغ في عام 1965. لم أستطع مقاومة الإغراء في وضع مرجع لاغي ، ببساطة لأنه كان من السهل التنفيذ". (المعنى العام هو: "أسميها اختراع فارغة خطأ لا تقدر بثمن. لأنه في برية أجهزة الكمبيوتر في عام 1965 ، كانت المراجع الفارغة سهلة التنفيذ ، لذلك لم أتمكن من مقاومة إغراء اختراع المؤشر الفارغ.").
ثم ، دعنا نرى ما هي المشكلات الأخرى التي ستقدمها.
تحقق من الرمز التالي:
عنوان السلسلة = person.getCountry (). getProvince (). getCity () ؛
إذا كنت قد لعبت بعض اللغات الوظيفية (Haskell ، Erlang ، Clojure ، Scala ، وما إلى ذلك) ، فإن ما سبق طريقة طبيعية للغاية في الكتابة. بالطبع ، يمكن تنفيذ طريقة الكتابة أعلاه باستخدام Java.
ولكن من أجل التعامل مع جميع الاستثناءات الفارغة الممكنة تمامًا ، يتعين علينا تغيير نموذج برمجة الوظائف الأنيق إلى هذا:
if (person! = null) {country country = person.getCountry () ؛ if (Country! = null) {Province Province = Country.getProvince () ؛ if (Province! = null) {address = province.getCity () ؛ }}}في لحظة ، عاد Java8 للبرمجة الوظيفية عالية الجودة إلى 10 سنوات. لا تزال هذه الأحكام المتداخلة تافهة ، مما يزيد من مبلغ الكود وعدم وجوده. على الأرجح أن يحدث: خلال معظم الوقت ، سوف ينسى الناس الحكم على الفارغ الذي قد يحدث ، حتى أن كبار السن الذين كتبوا رمزًا لسنوات عديدة ليسوا استثناءً.
يعد القسم أعلاه من المعالجة الفارغة ، وهو طبقة متداخلة من طبقة ، جزءًا من Java التقليدية التي تم انتقادها لفترة طويلة. إذا كنت تستخدم إصدارات Java المبكرة كلغة التنوير الخاصة بك ، فإن هذه الرائحة من Get-> إذا كانت العائدات Null-> ستؤثر عليك لفترة طويلة (تذكر في مجتمع أجنبي ، والتي تسمى: التنمية الموجهة للكيانات).
باستخدام اختياري لتنفيذ البرمجة الوظيفية Java
حسنًا ، بعد الحديث عن جميع أنواع المشاكل ، يمكننا إدخال حقبة جديدة.
قبل وقت طويل من إطلاق إصدار Java SE 8 ، كان لغات التطوير الوظيفية الأخرى المماثلة حلولها المختلفة. هنا رمز Groovy:
إصدار سلسلة = الكمبيوتر؟ .GetSoundCard ()؟
يستخدم Haskell معرف فئة ربما لمعالجة القيم الفارغة. توفر Scala ، والمعروفة باسم لغة تطوير متعددة البرامج ، خيارًا مشابهًا لربما ، لالتفاف والمعالجة الفارغة.
Java8 يقدم java.util.optional <T> للتعامل مع مشكلة فارغة للبرمجة الوظيفية. تتشابه أفكار المعالجة الخاصة بـ <T> الاختيارية بأفكار Haskell و Scala ، ولكن هناك بعض الاختلافات. لنلقي نظرة على المثال التالي لرمز جافا:
اختبار الفئة العامة {public static void main (string [] args) {Final String Text = "Hallo World!" ؛ اختياري. اختياري. } // طباعة واعتراض السلسلة بعد str [5] print static string print (String str) {system.out.println (str) ؛ إرجاع str.substring (6) ؛ }} // consol output // num1: Hallo World! // num2: World! // num3: // num4: Hallo World!(يمكنك نسخ الرمز أعلاه في IDE ، شريطة تثبيت JDK8.)
يتم إنشاء اختياريتين في الكود أعلاه ، والوظائف التي تم تنفيذها هي نفسها بشكل أساسي. كلاهما يستخدم اختياريًا كقذيفة السلسلة لاقتطاع السلسلة. عند مواجهة قيمة فارغة أثناء المعالجة ، لن تستمر المعالجة. يمكننا أن نجد أنه بعد ظهور S-> NULL في الثانية الاختيارية ، لن يتم تنفيذ ما بعد ذلك.
انتبه إلى الإخراج // num3: ، مما يعني أن الحرف "" هو الناتج ، وليس فارغًا.
يوفر اختياري واجهات غنية للتعامل مع المواقف المختلفة ، مثل تعديل الكود إلى:
اختبار الفئة العامة {public static void main (string [] args) {Final String Text = "Hallo World!" ؛ System.out.println (Sumrcase (text)) ؛ // الطريقة ذات الأحرف الصغيرة (null ، system.out :: println) ؛ // method two} static string smallcase (string str) {return incortal.ofnullable (str) .map (s -> s.tolowercase ()). map (s-> s.replace ("world" ، "java") } QUITCASE STATIC VOID (String str ، المستهلك <STRING> المستهلك) {consumer.accept (shallcase (str)) ؛ }} // output // hallo java! // nanوبهذه الطريقة ، يمكننا معالجة سلسلة ديناميكية. إذا تم العثور على القيمة لتكون خالية في أي وقت ، فإننا نستخدم Orelse لإرجاع الإعداد المسبق "NAN".
بشكل عام ، يمكننا أن نلف أي بنية بيانات بشكل اختياري ثم معالجتها بطريقة وظيفية دون الحاجة إلى القلق بشأن الفرق التي قد تظهر في أي وقت.
دعنا نلقي نظرة على كيفية profite.getCountry (). getProvince ().
الطريقة الأولى هي عدم تغيير الكيان السابق:
استيراد java.util.optional ؛ اختبار الطبقة العامة {public static void main (string [] args) {system.out.println (eptarical.ofnullable (new person ()) .map (x-> x.country) .map (x-> x.provinec) .map (x.city). .orelse ("unkonwn")) ؛ }} فئة شخص {country country ؛} clant {Province ProvisionEc ؛} Class {City City ؛} Class {String name ؛}هنا ، يتم استخدام اختياري كما تم إرجاع القشرة في كل مرة. إذا عاد موقف معين لاغية ، فستحصل على "unkonwn".
الطريقة الثانية هي تحديد جميع القيم في اختياري:
استيراد java.util.optional ؛ اختبار الطبقة العامة {public static void main (string [] args) {system.out.println (new person () .country.flatmap (x -> x.provinec) .flatmap (Prevince :: getCity) .flatmap (x - }} class person {اختياري <Tearny> country = potortal.empty () ؛} Class Country {اختياري <CROFINCE> PROVISIONC ؛} Class {اختياري <CTY> CITY ؛ اختياري <City> getCity () {// for :: return City ؛ }} Class City {اختياري <string> name ؛}يمكن دمج الطريقة الأولى بسلاسة مع Javabeans أو الكيان أو Poja الحالي دون أي تغييرات ، ويمكن دمجها بسهولة أكبر في واجهات الطرف الثالث (مثل حبوب الربيع). يوصى باستخدام الطريقة الاختيارية الأولى كطريقة رئيسية. بعد كل شيء ، لا يمكن لأي شخص في الفريق فهم الغرض من كل GET/SET مع اختياري.
يوفر اختياري أيضًا طريقة تصفية لتصفية البيانات (في الواقع ، توفر واجهات على غرار الدفق في Java 8 طرق المرشح). على سبيل المثال ، في الماضي حكمنا أن القيمة موجودة وجعلت المعالجة المقابلة:
if (Province! = null) {City City = Province.getCity () ؛ if (null! = City && "Guangzhou" .equals (city.getName ()) {system.out.println (city.getName ()) ؛} {system.out.println ("unkonwn") ؛}}}}الآن يمكننا تعديله إلى
اختياري.
في هذه المرحلة ، يتم الانتهاء من مقدمة البرمجة الوظيفية باستخدام اختياري. بالإضافة إلى الأساليب المذكورة أعلاه ، يوفر اختياري أيضًا طرقًا تعتمد على المزيد من الاحتياجات ، أو orelseget ، أو orelsethrow ، وما إلى ذلك. سوف يلقي Orelseget استثناء مؤشر فارغ بسبب القيمة الفارغة ، وسيقوم orelsethrow بإلقاء استثناء محدد من قبل المستخدم عند حدوث NULL. يمكنك عرض وثائق API لمعرفة المزيد حول جميع الطرق.
مكتوب في النهاية
الاختياري هو مجرد غيض من الجبل الجليدي للبرمجة الوظيفية Java. من الضروري الجمع بين ميزات مثل Lambda و Stream و FunctionInterface لفهم فعالية البرمجة الوظيفية Java8. أردت في الأصل تقديم بعض التعليمات البرمجية المصدر والمبادئ التشغيلية الاختيارية ، ولكن الاختياري نفسه يحتوي على رمز قليل جدًا وليس الكثير من واجهة API. إذا كنت تفكر في الأمر بعناية ، فلا يوجد ما تقوله وستحذفه.
على الرغم من أن اختياريًا أنيقًا ، إلا أنني شخصياً أشعر أن هناك بعض مشكلات الكفاءة ، لكن لم يتم التحقق منها بعد. إذا كان لدى أي شخص أي بيانات ، فيرجى إبلاغي بذلك.
أنا لست "مؤيدًا للبرمجة الوظيفية". من منظور مديري الفريق ، يتم زيادة كل صعوبة في التعلم ، ستكون تكلفة استخدام الموظفين وتفاعل الفريق أعلى. تمامًا كما هو الحال في Legend ، يمكن أن يكون لدى Lisp رمز أقل ثلاثين مرة من C ++ وهو أكثر كفاءة في التطوير ، ولكن إذا كانت شركة تكنولوجيا المعلومات التقليدية المحلية تستخدم حقًا لمشاريع ، إلى أين تذهب وكم تكلفة الحصول على هؤلاء الأشخاص الذين يستخدمون LISP؟
لكنني أشجع الجميع بشدة على تعلم وفهم أفكار البرمجة الوظيفية. لا سيما المطورين الذين اعتادوا على الغزو من قبل Java وما زالوا لا يعرفون التغييرات التي ستجلبها Java8 ، Java8 فرصة جيدة. كما يتم تشجيعه على تقديم ميزات Java8 الجديدة في المشروع الحالي. يحتاج الفريق الذي يتعاون لفترة طويلة ولغة البرمجة القديمة إلى ضخ حيوية جديدة باستمرار ، وإلا فإنك ستتراجع إذا لم تقدم.
ما سبق هو مجموعة المعلومات الاختيارية Java. سنستمر في إضافة المعلومات ذات الصلة في المستقبل. شكرا لدعمكم لهذا الموقع!