مقدمة
NPE (NullPointerException) هو الاستثناء الأكثر شيوعًا في برامج تصحيح الأخطاء. لدى Google الكثير من المناقشات حول ما إذا كانت الطريقة يجب أن تعيد NULL أو جديد كائن فارغ.
في بداية المقالة ، دعنا نتحدث عن مشكلة NPE أولاً. مشكلة NPE هي NullPointerException التي نواجهها غالبًا في التطوير. لنفترض أن لدينا فئتان ، ويظهر مخطط فئة UML في الشكل التالي
في هذه الحالة ، هناك الرمز التالي
user.getAddress (). getProvince () ؛
قد تقوم طريقة الكتابة هذه بالإبلاغ عن nullpointerxception عندما يكون المستخدم لاغيرًا. من أجل حل هذه المشكلة ، يتم اعتماد طريقة الكتابة التالية
if (user! = null) {address = user.getAdDress () ؛ if (address! = null) {string province = address.getProvince () ؛ }}أسلوب الكتابة هذا قبيح نسبيا. من أجل تجنب أسلوب الكتابة القبيح أعلاه ، يصبح التصميم القبيح أنيقًا. يوفر Java8 فئة اختيارية لتحسين طريقة الكتابة هذه ، وسيتم شرح قسم النص التالي بالتفصيل.
مقدمة API
اسمحوا لي أولاً تقديم واجهة برمجة التطبيقات. على عكس المقالات الأخرى ، تتبنى هذه المقالة طريقة تشبيه وتجمع بين الكود المصدري. على عكس المقالات الأخرى ، تجعل كل قوائم API الأشخاص غير قادرين على العثور على النقاط الرئيسية.
(1) اختياري (قيمة t) ، فارغة () ، من (قيمة t) ، أو ofnullable (قيمة t)
هذه الوظائف الأربع لها ارتباطات ، لذلك يتم وضعها في مجموعة للذاكرة.
اسمحوا لي أولاً أن أشرح أن القيمة الاختيارية (T القيمة t) ، أي المُنشئ ، هي إذن خاص ولا يمكن استدعاؤه خارجيًا. الوظائف الثلاث الأخرى هي أذونات عامة لنا للاتصال بها. بعد ذلك ، يتمثل جوهر الاختياري في تخزين قيمة حقيقية داخليًا ، وعند الإنشاء ، يتم الحكم عليها بشكل مباشر ما إذا كانت قيمتها فارغة. حسنًا ، هذا لا يزال مجردة تمامًا. قم بتحميل الكود المصدري المباشر للمشارك الاختياري (t value) ، كما هو موضح في الشكل أدناه
ثم ، رمز المصدر لـ (t value) كما يلي
ثابت عام <T> اختياري <T> من (قيمة t) {إرجاع جديد اختياري <> (القيمة) ؛ } بمعنى آخر ، يسمى المنشئ داخليًا من خلال وظيفة (قيمة t). استنادًا إلى الكود المصدري للمنشئ ، يمكننا استنتاج استنتاجين:
(1) عندما تكون قيمة القيمة فارغة ، سيظل الإبلاغ عن nullpointerxception.
(2) يمكن إنشاء الكائن الاختياري الذي تم إنشاؤه بواسطة وظيفة (قيمة t) بشكل طبيعي عندما لا تكون قيمة القيمة فارغة.
بالإضافة إلى ذلك ، تحافظ الفئة الاختيارية أيضًا على كائن ذي قيمة خالية ، وربما مثل ما يلي
الفئة النهائية العامة اختياري <T> {// shemit ...... private static نهائي اختياري <؟> فارغ = جديد اختياري <> () ؛ خاص اختياري () {this.value = null ؛ } // shf ... static static <T> اختياري <T> فارغ () {suppressWarnings ("unchected") اختياري <T> t = (اختياري <T>) فارغ ؛ العودة ر ؛ }} ثم ، وظيفة فارغة () هي إرجاع الكائن الفارغ.
حسنًا ، تم وضع الكثير من الاستعدادات. يمكن القول أن Ofnullable (قيمة t) لها الوظيفة ، ويتم إضافة رمز المصدر.
static static <T> اختياري <T> Ofnullable (t value) {return value == null؟ فارغة (): من (القيمة) ؛ } حسنًا ، يجب أن يفهم الجميع ما يعنيه. الفرق مقارنة بـ (قيمة t) هو أنه عندما تكون قيمة القيمة لاغية ، فإن (قيمة t) ستقوم بالإبلاغ عن nullpointerxception ؛ لن ترمي Ofnullable (القيمة t) استثناء ، من Nullable (t value) لإرجاع كائن فارغ مباشرة.
هل هذا يعني أننا نستخدم الوظيفة البارزة فقط بدلاً من الوظيفة في مشروعنا؟
لا ، إذا كان هناك شيء ما ، فسيكون له قيمة بشكل طبيعي. عندما نركض ، لا نريد إخفاء NullPointerxception. بدلاً من ذلك ، تحتاج إلى الإبلاغ على الفور ، وفي هذه الحالة ، استخدم الوظيفة. لكن يجب أن أعترف أن هناك القليل من المشاهد. استخدم المدون هذه الوظيفة فقط في كتابة حالات اختبار Junit.
(2) orelse (t other) ، orelseget (المورد <؟ يمتد t> آخر) و orelsethrow (المورد <؟
يتم حفظ هذه الوظائف الثلاث في مجموعة ، ويتم استدعاؤها عندما تكون القيمة التي يتم تمريرها بواسطة المُنشئ فارغة. استخدام Orelse و Orelseget كما يلي. عندما تكون القيمة لاغية ، يتم إعطاء قيمة افتراضية:
testpublic void test () {user user = null ؛ user = potoryal.ofNullable (user) .orelse (createUser ()) ؛ user = potortal.ofNullable (user) .orelSeget (() -> createUser ()) ؛ } المستخدم العام createUser () {user user = new user () ؛ user.setName ("Zhangsan") ؛ إرجاع المستخدم ؛} الفرق بين هاتين الوظيفتين: عندما لا تكون قيمة المستخدم فارغة ، ستظل وظيفة orelse تنفذ طريقة CreateUser () ، في حين أن وظيفة orelseget لن تنفذ طريقة CreateUser () ، بحيث يمكنك اختبارها بنفسك.
أما بالنسبة لـ Orelsethrow ، عندما تكون القيمة لاغية ، يتم طرح استثناء مباشرة. الاستخدام كما يلي
مستخدم المستخدم = null ؛ اختياري.
(3) الخريطة (وظيفة <؟ super t ،؟ يمتد u> mapper) و flatmap (وظيفة <؟ super t ، اختياري <u>> mapper)
يتم وضع هاتين الوظيفتين في مجموعة من الذاكرة ، وهاتين وظيفتين تقومان بتشغيل القيم تحويل.
تحميل رمز المصدر مباشرة
الفئة النهائية العامة اختياري <T> {// shemit ...... public <u> اختياري <u> خريطة (وظيفة <؟ super t ،؟ يمتد U> mapper) {objects.requirenonnull (mapper) ؛ if (! ispresent ()) إرجاع فارغ () ؛ else {return eptarical.ofNullable (mapper.apply (value)) ؛ }} // shife ... public <u> اختياري <u> flatmap (وظيفة <؟ super t ، اختياري <u>> mapper) {objects.requirenonnull (mapper) ؛ if (! ispresent ()) إرجاع فارغ () ؛ else {return objects.requirenonnull (mapper.apply (value)) ؛ }}} لا يوجد فرق بين هاتين وظيفتين في جسم الوظيفة. الفرق الوحيد هو معلمة الدخول. نوع المعلمة الإدخال المقبولة بواسطة وظيفة الخريطة هي الدالة <؟ سوبر تي ،؟ يمتد u> ، في حين أن نوع معلمة الإدخال من flapmap هو الدالة <؟ Super T ، اختياري <u>>.
من حيث الاستخدام المحدد ، للخريطة:
إذا كانت بنية المستخدم كما يلي
مستخدم الفئة العامة {اسم السلسلة الخاصة ؛ السلسلة العامة getName () {return name ؛ }}في هذا الوقت ، فإن طريقة الكتابة لأخذ الاسم هي كما يلي
string city = antarial.ofnullable (user) .map (u-> u.getName ()). get () ؛
ل flatmap:
إذا كانت بنية المستخدم كما يلي
مستخدم الفئة العامة {اسم السلسلة الخاصة ؛ public اختياري <string> getName () {return eptarical.ofnullable (name) ؛ }}في هذا الوقت ، فإن طريقة الكتابة لأخذ الاسم هي كما يلي
string city = intersal.ofnullable (user) .flatmap (u-> u.getName ()). get () ؛
(4) ispresent () و ifpresent (المستهلك <؟ super t> المستهلك)
ضع هاتين وظيفتين معًا وحفظهما. تعني Ispresent تحديد ما إذا كانت القيمة فارغة ، وإذا كان الدعين يعني القيام ببعض العمليات عندما لا تكون القيمة فارغة. رموز المصدر لهاتين وظيفتين هي كما يلي
الفئة النهائية العامة اختياري <T> {// shfer ...... BOOLEAN BOOLEAN ISPRESENT () {return value! = null ؛ } // shemit ... public void ifpresent (المستهلك <؟ super t> المستهلك) {if (value! = null) consumer.accept (value) ؛ }}يحتاج إلى شرح إضافي ، لا
if (user! = null) {// todo: افعل شيئًا}مكتوب
مستخدم المستخدم = اختياري.
لهذا السبب ، لا يزال بنية الكود قبيحة. سيعطي المدون طريقة الكتابة الصحيحة لاحقًا
بالنسبة إلى ifpresent (المستهلك <؟ super t> المستهلك) ، فإن الاستخدام بسيط للغاية ، كما هو موضح أدناه
اختياري.
(5) مرشح (مسند <؟ super t> يتنبأ)
بدون مزيد من اللغط ، فقط قم بتحميل الكود المصدري
الفئة النهائية العامة اختيارية <T> {// shfer ...... objects.requirenonnull (predicate) ؛ إذا (! ispresent ()) إرجاع هذا ؛ عائد آخر تنبؤ. الاختبار (القيمة)؟ هذا: فارغ () ؛} تقبل طريقة المرشح مسندًا لتصفية القيم الموجودة في اختياري. إذا كانت القيم المضمنة تفي بالشروط ، فسيظل يتم إرجاع الاختياري ؛ خلاف ذلك ، سيتم إرجاع الاختياري.
الاستخدام كما يلي
اختياري <Sether> user1 = antarial.OfNullable (user) .filter (u -> u.getName (). length () <6) ؛
كما هو موضح أعلاه ، إذا كان طول اسم المستخدم أقل من 6 ، ثم العودة. إذا كان أكبر من 6 ، يتم إرجاع كائن فارغ.
الاستخدام العملي
مثال 1
في طريقة الوظيفة
الكتابة السابقة
السلسلة العامة getCity (مستخدم المستخدم) يلقي الاستثناء {if (user! = null) {if (user.getAdDress ()! = null) {address = user.getAddress () ؛ if (address.getCity ()! = null) {return address.getCity () ؛ }}} رمي عملية عرض جديدة ("خطأ القيمة") ؛ }طريقة الكتابة Java8
السلسلة العامة getCity (مستخدم المستخدم) يلقي الاستثناء {return experial.ofnullable (user) .map (u-> u.getAddress ()) .map (a-> a.getCity ()) .orelsethrow (()-> استثناء جديد ("خطأ في الجلب")) ؛}}مثال 2
على سبيل المثال ، في البرنامج الرئيسي
الكتابة السابقة
if (user! = null) {dosomething (user) ؛}طريقة الكتابة Java8
اختياري.
مثال 3
الكتابة السابقة
المستخدم العام getUser (المستخدم) يلقي الاستثناء {if (user! = null) {string name = user.getName () ؛ if ("Zhangsan" .equals (name)) {return user ؛ }} آخر {user = new user () ؛ user.setName ("Zhangsan") ؛ إرجاع المستخدم ؛ }}طريقة الكتابة Java8
المستخدم العام getUser (مستخدم المستخدم) {return eptarical.ofnullable (user) .filter (u-> "Zhangsan" .equals (u.getName ())) .orelseget (()-> {user user1 = new user () ؛ user1.setName ("Zhangsan") ؛لن يتم إدراج أمثلة أخرى واحدة تلو الأخرى. ومع ذلك ، يعتقد المدون أن استخدام برمجة السلسلة هذه أنيقة بالفعل في الرمز. ومع ذلك ، فإن المنطق ليس واضحًا جدًا ويتم تقليل قابلية القراءة. نستخدمه وفقًا للوضع في المشروع.
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.