الفصل 3 السلاسل والمقارنات والمرشحات
بعض الطرق التي يقدمها JDK مفيدة جدًا لكتابة كود النمط الوظيفي. نحن بالفعل على دراية ببعض الفئات والواجهات في مكتبة JDK، مثل String. ومن أجل التخلص من النمط القديم الذي اعتدنا عليه، علينا أن نبحث بنشاط عن فرص لاستخدام هذه الأساليب الجديدة. وبالمثل، عندما نحتاج إلى استخدام فئة داخلية مجهولة بطريقة واحدة فقط، يمكننا الآن استبدالها بتعبير لامدا، دون الحاجة إلى كتابتها مرهقة كما كان من قبل.
في هذا الفصل، سوف نستخدم تعبيرات لامدا ومراجع الأساليب لاجتياز السلاسل، وتنفيذ واجهة المقارنة، وعرض الملفات في الدليل، ومراقبة التغييرات في الملفات والأدلة. ستستمر بعض الأساليب التي تم تقديمها في الفصل السابق في الظهور هنا لمساعدتنا في إكمال هذه المهام بشكل أفضل. ستساعدك التقنيات الجديدة التي تتعلمها في تحويل التعليمات البرمجية الطويلة والمملة إلى شيء موجز وسريع التنفيذ وسهل الصيانة.
التكرار على السلسلة
طريقة chars() هي طريقة جديدة في فئة String، وهي جزء من واجهة CharSequence. إنها أداة مفيدة جدًا إذا كنت تريد اجتياز تسلسل أحرف السلسلة بسرعة. باستخدام هذا المُكرِّر الداخلي، يمكننا العمل بسهولة على كل حرف في السلسلة. حاول استخدامه لمعالجة سلسلة أولاً. فيما يلي بعض الطرق لاستخدام مراجع الطريقة.
انسخ رمز الكود كما يلي:
السلسلة النهائية str = "w00t";
str.chars()
.forEach(ch -> System.out.println(ch));
تقوم طريقة chars() بإرجاع كائن Stream، والذي يمكننا استخدام مكرره الداخلي forEach() لاجتيازه. في المكرر، يمكننا الوصول مباشرة إلى الأحرف الموجودة في السلسلة. يوجد أدناه إخراج التكرار عبر السلسلة وطباعة كل حرف.
انسخ رمز الكود كما يلي:
119
48
48
116
وهذه ليست النتيجة التي نريدها. نتوقع أن نرى الحروف، ولكن الناتج هو أرقام. وذلك لأن الأسلوب chars() يُرجع دفقًا صحيحًا بدلاً من نوع الحرف. دعونا أولاً نفهم واجهة برمجة التطبيقات هذه ثم نقوم بتحسين نتائج المخرجات.
في الكود السابق، قمنا بإنشاء تعبير لامدا كمعلمة إدخال لطريقة forEach. فهو ببساطة يقوم بتمرير المعلمات إلى طريقة println(). نظرًا لأن هذه العملية شائعة جدًا، فيمكننا استخدام مترجم Java لتبسيط هذا الرمز. تمامًا كما هو الحال في استخدام مراجع الطريقة في الصفحة 25، استبدلها بمرجع الطريقة ودع المترجم يقوم بتوجيه المعلمات نيابةً عنا.
لقد رأينا كيفية إنشاء مرجع أسلوب لأسلوب مثيل. على سبيل المثال، طريقة name.toUpperCase()، ومرجع الطريقة هو String::toUpperCase. في المثال التالي، نقوم باستدعاء أسلوب مثيل يشير بشكل ثابت إلى System.out. يمكن أن يكون الجانب الأيسر من النقطتين المشار إليهما بواسطة الطريقة اسم فئة أو تعبير. مع هذه المرونة، يمكننا بسهولة إنشاء مرجع إلى طريقة println()، كما هو موضح أدناه.
انسخ رمز الكود كما يلي:
str.chars()
.forEach(System.out::println);
كما ترون، يمكن لمترجم Java إكمال توجيه المعلمات بذكاء شديد. تذكر أن تعبيرات لامدا ومراجع الطريقة يمكن أن تظهر فقط عند تلقي واجهة وظيفية، وسيقوم مترجم Java بإنشاء طريقة مقابلة هناك (تعليق توضيحي: سيقوم المترجم بإنشاء تطبيق للواجهة الوظيفية، والذي يحتوي على طريقة واحدة فقط. ). تشير الطريقة التي استخدمناها من قبل إلى String::toUpperCase، وستصبح المعلمات التي تم تمريرها إلى الطريقة التي تم إنشاؤها في النهاية الكائن الهدف لاستدعاء الطريقة، مثل هذا: Parameter.toUpperCase(). وذلك لأن مرجع الطريقة يعتمد على اسم الفئة (سلسلة). يعتمد مرجع الطريقة في المثال أعلاه على تعبير، وهو مثيل لـ PrintStream ويتم الرجوع إليه من خلال System.out. نظرًا لوجود كائن استدعاء الأسلوب بالفعل، يقرر مترجم Java استخدام المعلمات في الطريقة التي تم إنشاؤها كمعلمات لأسلوب println هذا: System.out.println(name).
(تعليق توضيحي: في الواقع، هناك سيناريوهان بشكل أساسي. يتم أيضًا تمرير مرجع الطريقة. أحدهما هو الكائن الذي تم اجتيازه، وبالطبع الكائن الهدف لاستدعاء الطريقة، مثل name.toUpperCase، والآخر يستخدم كمعلمة لـ استدعاء الأسلوب، مثل System.println(name).)
يصبح الكود أبسط بكثير بعد استخدام مراجع الطريقة، ولكننا نحتاج إلى فهم أعمق لكيفية عمله. بمجرد أن نتعرف على مراجع الطرق، يمكننا معرفة توجيه المعلمات بأنفسنا.
على الرغم من أن الكود الموجود في هذا المثال موجز بما فيه الكفاية، إلا أن الإخراج لا يزال غير مرضٍ. كنا نتوقع أن نرى الحروف ولكن الأرقام ظهرت بدلا من ذلك. من أجل حل هذه المشكلة، دعونا نكتب طريقة لإخراج int كأحرف.
انسخ رمز الكود كما يلي:
طباعة باطلة ثابتة خاصة (int aChar) {
System.out.println((شار)(aChar));
}
يمكن أن يؤدي استخدام مراجع الطريقة إلى تحسين نتائج الإخراج بسهولة.
انسخ رمز الكود كما يلي:
str.chars()
.forEach(IterateString::printChar);
الآن، على الرغم من أن النتيجة التي يتم إرجاعها بواسطة chars() هي int، فلا يهم عندما نحتاج إلى الطباعة، سنقوم بتحويلها إلى أحرف. هذه المرة الإخراج هو في النهاية الحروف.
انسخ رمز الكود كما يلي:
ث
0
0
ر
إذا أردنا معالجة الأحرف بدلاً من ints من البداية، فيمكننا تحويل ints مباشرةً إلى أحرف بعد استدعاء الأحرف:
انسخ رمز الكود كما يلي:
str.chars()
.mapToObj(ch -> Character.valueOf((char)ch))
.forEach(System.out::println);
نستخدم هنا مكررًا داخليًا للدفق الذي يتم إرجاعه بواسطة chars(). بالطبع، يمكن استخدام أكثر من هذه الطريقة. بعد الحصول على كائن الدفق، يمكننا استخدام أساليبه، مثل الخريطة () والتصفية () والتقليل () وما إلى ذلك. يمكننا استخدام طريقة التصفية () لتصفية الأحرف التي هي أرقام:
انسخ رمز الكود كما يلي:
str.chars()
.filter(ch -> Character.isDigit(ch))
.forEach(ch -> printChar(ch));
عند الإخراج بهذه الطريقة، يمكننا رؤية الأرقام فقط:
انسخ رمز الكود كما يلي:
0
0
وبالمثل، بالإضافة إلى تمرير تعبيرات lambda إلى أساليب filter() وforEach()، يمكننا أيضًا استخدام مراجع الطرق.
انسخ رمز الكود كما يلي:
str.chars()
.filter(الحرف::isDigit)
.forEach(IterateString::printChar);
يلغي مرجع الطريقة هنا توجيه المعلمات الزائدة عن الحاجة. في هذا المثال، نرى أيضًا استخدامًا مختلفًا عن الطريقتين السابقتين. في المرة الأولى التي أشرنا فيها إلى طريقة مثيل، في المرة الثانية كانت طريقة مرجعية ثابتة (System.out). هذه المرة هي إشارة إلى طريقة ثابتة - كانت مراجع الطريقة تدفع بصمت.
تبدو جميع المراجع إلى أساليب المثيل والأساليب الثابتة متشابهة: على سبيل المثال، String::toUpperCase وCraft::isDigit. يحدد المترجم ما إذا كانت الطريقة هي طريقة مثيل أو طريقة ثابتة لتحديد كيفية توجيه المعلمات. إذا كانت طريقة مثيل، فسوف تستخدم معلمات الإدخال للطريقة التي تم إنشاؤها ككائن هدف لاستدعاء الطريقة، مثل المعلمة toUpperCase () (بالطبع هناك استثناءات، مثل الكائن الهدف لاستدعاء الطريقة). تم تحديده، مثل System::out.println ()). بالإضافة إلى ذلك، إذا كانت طريقة ثابتة، فسيتم استخدام معلمات الإدخال للطريقة التي تم إنشاؤها كمعلمات للطريقة المشار إليها، مثل Character.isDigit(parameter). يحتوي الملحق 2 في الصفحة 152 على إرشادات مفصلة حول كيفية استخدام مراجع الطريقة وبناء الجملة الخاصة بها.
على الرغم من أن مراجع الطريقة ملائمة للاستخدام، إلا أنه لا تزال هناك مشكلة - الغموض الناتج عن تعارض أسماء الطرق. إذا كانت طريقة المطابقة عبارة عن طريقة مثيل وطريقة ثابتة، فسيقوم المترجم بالإبلاغ عن خطأ بسبب غموض الطريقة. على سبيل المثال، إذا كتبنا Double::toString بهذه الطريقة، فإننا نريد بالفعل تحويل نوع مزدوج إلى سلسلة، لكن المترجم لا يعرف ما إذا كان يجب استدعاء طريقة مثيل السلسلة العامة toString() أو استدعاء السلسلة الثابتة العامة toString (مزدوج)، لأن كلا الطريقتين من فئة مزدوجة. إذا واجهت مثل هذا الموقف، فلا تثبط عزيمتك، فقط استخدم تعبيرات لامدا لإكماله.
بمجرد أن نعتاد على البرمجة الوظيفية، يمكننا التبديل ذهابًا وإيابًا بين تعبيرات لامدا ومراجع الطريقة حسب الرغبة.
نستخدم في هذا القسم طريقة جديدة في Java 8 للتكرار عبر السلاسل. دعونا نلقي نظرة على التحسينات التي أدخلت على واجهة المقارنة.