معامل الـ instanceof
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 31 من 87.
أظهر الدرس السابق أن عملية التحويل لأسفل (downcasting) يمكن أن تفشل بـ ClassCastException إذا لم يكن الكائن في الواقع من النوع الذي تقوم بالتحويل إليه. يقوم عامل instanceof بحل هذه المشكلة من خلال السماح لك بالتحقق من نوع الكائن قبل عملية التحويل.
الصيغة البرمجية بسيطة: object instanceof ClassName تُرجع true إذا كان الكائن نسخة من تلك الفئة (أو أي من فئاتها الفرعية):
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark(); // من الآمن استدعاء الأساليب (methods) الخاصة بـ Dog
}يمنع هذا الفحص أخطاء وقت التشغيل من خلال ضمان نجاح عملية التحويل (cast). كما يعيد المعامل القيمة true للأنواع الأب في التسلسل الهرمي:
Dog dog = new Dog();
System.out.println(dog instanceof Dog); // true
System.out.println(dog instanceof Animal); // true
System.out.println(dog instanceof Object); // trueمنذ إصدار Java 16، يمكنك دمج التحقق والتحويل (cast) في خطوة واحدة باستخدام مطابقة الأنماط (pattern matching):
if (animal instanceof Dog dog) {
dog.bark(); // 'dog' تم تحويله بالفعل وجاهز للاستخدام
}هذا البناء البرمجي الأكثر بساطة يلغي الحاجة لسطر تحويل النوع (cast) المنفصل. لاحظ أن instanceof يعيد false عند اختباره مقابل null، لذا لا تحتاج إلى فحص null بشكل منفصل.
التحدي
سهللنقم ببناء نظام فحص للمركبات يستخدم عامل التشغيل instanceof لتحديد أنواع المركبات المختلفة والتعامل معها بأمان. ستقوم بإنشاء تسلسل هرمي للمركبات وكتابة كود يتحقق من النوع الفعلي لكل مركبة قبل الوصول إلى ميزاتها المتخصصة.
ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:
Vehicle.java: قم بإنشاء الفئة الأساسية التي تشترك فيها جميع المركبات. كل مركبة لها حقلmodelمن نوع (String). قم بتضمين منشئ (constructor) لتهيئته، وطريقة جلبgetModel()، وطريقةinspect()التي تطبع:"Inspecting vehicle: " + modelCar.java: قم بإنشاء فئة ترث من Vehicle. تمتلك السيارات حقلاً إضافياًnumDoorsمن نوع (int). استخدمsuperلتهيئة الفئة الأب. أضف طريقةcheckDoors()التي تطبع:getModel() + " has " + numDoors + " doors"Motorcycle.java: قم بإنشاء فئة أخرى ترث من Vehicle. تمتلك الدراجات النارية حقلاًhasSidecarمن نوع (boolean). أضف طريقةcheckSidecar()التي تطبع إماgetModel() + " has a sidecar"أوgetModel() + " has no sidecar"بناءً على قيمة الـ boolean.Main.java: هنا يصبحinstanceofضرورياً! ستتلقى أربعة مدخلات: طراز السيارة، عدد الأبواب، طراز الدراجة النارية، وما إذا كانت تحتوي على عربة جانبية (true/false).قم بإنشاء مصفوفة من مراجع
Vehicleتحتوي على Car و Motorcycle. ثم قم بالمرور عبر المصفوفة ولكل مركبة:- استدعِ طريقة
inspect()الأساسية - استخدم
instanceofللتحقق مما إذا كانت Car - إذا كان الأمر كذلك، قم بعمل downcast بأمان واستدعِcheckDoors() - استخدم
instanceofللتحقق مما إذا كانت Motorcycle - إذا كان الأمر كذلك، قم بعمل downcast بأمان واستدعِcheckSidecar()
- استدعِ طريقة
ستتلقى أربعة مدخلات: طراز السيارة (String)، عدد الأبواب (int)، طراز الدراجة النارية (String)، ووجود عربة جانبية (boolean).
يجب أن يظهر مخرجك أربعة أسطر - لكل مركبة، سترى رسالة الفحص الخاصة بها متبوعة بالفحص الخاص بنوعها. يضمن عامل التشغيل instanceof أنك تستدعي طرق Car فقط على كائنات Car الفعلية وطرق Motorcycle فقط على كائنات Motorcycle الفعلية، مما يمنع حدوث أي ClassCastException!
ورقة مرجعية
يتحقق عامل التشغيل instanceof مما إذا كان الكائن مثيلاً لفئة معينة قبل إجراء التحويل (casting):
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark(); // Safe to call Dog-specific methods
}يعيد عامل التشغيل القيمة true لفئة الكائن وجميع الفئات الأب في التسلسل الهرمي:
Dog dog = new Dog();
System.out.println(dog instanceof Dog); // true
System.out.println(dog instanceof Animal); // true
System.out.println(dog instanceof Object); // trueمنذ إصدار Java 16، يجمع مطابقة الأنماط (pattern matching) بين التحقق والتحويل في خطوة واحدة:
if (animal instanceof Dog dog) {
dog.bark(); // 'dog' is already cast and ready to use
}يعيد عامل التشغيل instanceof القيمة false عند اختباره مقابل null، مما يلغي الحاجة إلى عمليات تحقق منفصلة من القيم الفارغة (null checks).
جرّب بنفسك
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// قراءة المدخلات
String carModel = scanner.nextLine();
int numDoors = Integer.parseInt(scanner.nextLine());
String motorcycleModel = scanner.nextLine();
boolean hasSidecar = Boolean.parseBoolean(scanner.nextLine());
// TODO: إنشاء كائن Car باستخدام carModel و numDoors
// TODO: إنشاء كائن Motorcycle باستخدام motorcycleModel و hasSidecar
// TODO: إنشاء مصفوفة من مراجع Vehicle تحتوي على Car و Motorcycle
// TODO: التكرار عبر المصفوفة ولكل مركبة:
// 1. استدعاء الدالة inspect()
// 2. استخدام instanceof للتحقق مما إذا كانت Car، ثم عمل downcast واستدعاء checkDoors()
// 3. استخدام instanceof للتحقق مما إذا كانت Motorcycle، ثم عمل downcast واستدعاء checkSidecar()
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
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أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر