الرموز البديلة (?, extends, super)
جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 57 من 87.
تقيّد معلمات النوع bounded الأنواعَ العامة بأنواع محددة، ولكن ماذا يحدث عندما تعمل مع أنواع عامة موجودة ولا تعرف معلمة النوع الدقيقة، أو لا تهتم بها؟ هنا يأتي دور wildcards.
يمثّل unbounded wildcard ? نوعًا غير معروف. يكون مفيدًا عندما تريد قبول أي نوع مُعلَّم بالمعاملات:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
printList(new ArrayList<String>()); // يعمل
printList(new ArrayList<Integer>()); // يعملتقبل البدل العام ذي الحدّ العلوي ? extends Type النوع المحدد أو أيًا من فئاته الفرعية. استخدمه عندما تحتاج إلى القراءة من بنية عامة:
public static double sumNumbers(List<? extends Number> numbers) {
double sum = 0;
for (Number n : numbers) {
sum += n.doubleValue();
}
return sum;
}
sumNumbers(Arrays.asList(1, 2, 3)); // قائمة من Integer
sumNumbers(Arrays.asList(1.5, 2.5)); // قائمة من Doubleيقبل المعيّن العام ذي الحد الأدنى ? super Type النوع المحدد أو أيًّا من فئاته الفائقة. استخدمه عندما تحتاج إلى الكتابة في بنية عامة:
public static void addIntegers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
List<Number> numbers = new ArrayList<>();
addIntegers(numbers); // يعمل - Number هو نوع أعلى (supertype) من Integerإرشاد مفيد: استخدم extends عندما تقرأ فقط (منتِج)، واستخدم super عندما تكتب فقط (مستهلك). يُعرف هذا بمبدأ PECS: Producer Extends، Consumer Super.
التحدي
سهللنبنِ نظامًا لإطعام حيوانات حديقة حيوان يوضّح الأنواع الثلاثة من wildcards! ستنشئ تسلسلاً هرميًا للحيوانات والطعام، ثم تكتب أساليب مساعدة تستخدم wildcards غير المقيّدة والمقيّدة بحد علوي والمقيّدة بحد سفلي للتعامل مع سيناريوهات إطعام مختلفة.
ستنظّم التعليمات البرمجية عبر أربعة ملفات:
Food.java: أنشئ تسلسلاً هرميًا بسيطًا من class للطعام. ابدأ بـ class أساسيFoodيحتوي على حقل خاصnameمن النوع (String)، وconstructor لتهيئته، وأسْلوبgetName(). ثم أنشئ subclassين في الملف نفسه:Meatالذي يعملextends Food(ويستدعي الـconstructorsuper(name)) وVegetableالذي يعملextends Food(بالنمط نفسه).Animal.java: أنشئ class أساسيًاAnimalيحتوي على حقل خاصspeciesمن النوع (String)، وconstructor، وأسْلوبgetSpecies(). أنشئ subclassين:CarnivoreوHerbivore، بحيث يعمل كل منهماextends Animalويستدعي constructor الخاص بـ parent.ZooFeeder.java: هنا تظهر قوة wildcards! أنشئ utility class تحتوي على ثلاثة static methods:printInventory(List<?> items)- تستخدم wildcard غير مقيّدة لطباعة أي قائمة. نفّذ loop عبر العناصر واطبع كل item باستخدامtoString()، عنصرًا واحدًا في كل line.calculateTotalFood(List<? extends Food> foods)- تستخدم wildcard مقيّدة بحد علوي للقراءة من قائمة تحتوي على أي نوع من Food. أعد count العناصر على هيئةint. يوضّح هذا القراءة من producer.addMeatToStock(List<? super Meat> stock, String meatName)- تستخدم wildcard مقيّدة بحد سفلي للكتابة إلى قائمة يمكنها احتواء Meat. أنشئ object جديدًا منMeatبالاسم المعطى وأضفه إلى stock. يوضّح هذا الكتابة إلى consumer.Main.java: اجمع نظام إطعام حديقة الحيوان! ستتلقى inputين: اسم اللحم (String) واسم الخضار (String).أولًا، أنشئ
ArrayList<Food>وأضف object منMeatباسم اللحم وobject منVegetableباسم الخضار. اطبعFood inventory:ثم استدعِprintInventoryمع هذه القائمة.بعد ذلك، اطبع blank line، ثم اطبع
Total food items: [count]باستخدامcalculateTotalFoodعلى قائمة الطعام.أخيرًا، أنشئ
ArrayList<Food>جديدة باسمmeatStock. اطبع blank line، ثمAdding to meat stock.... استدعِaddMeatToStockمع هذه القائمة والسلسلة النصية"Beef". ثم اطبعMeat stock after adding:واستدعِprintInventoryعلى meat stock.
ستتلقى inputين بالترتيب: اسم اللحم واسم الخضار.
طبّق Override على toString() في class الـFood لإرجاع اسم الطعام، حتى تعمل الطباعة بشكل صحيح. لاحظ كيف يخدم كل نوع من wildcard غرضًا مختلفًا: ? لتحقيق أقصى قدر من المرونة عندما تحتاج فقط إلى القراءة على هيئة Object، و? extends عند قراءة أنواع محددة، و? super عند الكتابة إلى collection!
جرّب بنفسك
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String meatName = scanner.nextLine();
String vegetableName = scanner.nextLine();
// TODO: إنشاء ArrayList<Food> وإضافة كائنات Meat و Vegetable
// TODO: Print "Food inventory:" then call printInventory
// TODO: Print blank line, then "Total food items: [count]" using calculateTotalFood
// TODO: إنشاء ArrayList<Food> جديد باسم meatStock
// TODO: Print blank line, then "Adding to meat stock..."
// TODO: استدعاء addMeatToStock مع meatStock و "Beef"
// TODO: Print "Meat stock after adding:" then call printInventory on meatStock
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت