أهلاً! في درس سابق، ناقشنا صب الأنواع البدائية. دعونا نتذكر بإيجاز ما تمت مناقشته. لقد تخيلنا الأنواع البدائية (في هذه الحالة، الأنواع الرقمية) كدمى متداخلة تختلف في الحجم وفقًا لحجم الذاكرة التي تشغلها. كما تتذكر، فإن وضع دمية أصغر داخل دمية أكبر هو أمر بسيط سواء في الحياة الواقعية أو في برمجة Java.
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
short smallNumber = (short) bigNumber;
System.out.println(smallNumber);
}
}
وهذا مثال على التحويل التلقائي أو التوسيع . يحدث ذلك من تلقاء نفسه، لذلك لا تحتاج إلى كتابة تعليمات برمجية إضافية. في النهاية، نحن لا نفعل أي شيء غير عادي: نحن فقط نضع دمية أصغر في دمية أكبر. إنها مسألة أخرى إذا حاولنا أن نفعل العكس ونضع دمية روسية أكبر في دمية أصغر. لا يمكنك فعل ذلك في الحياة الواقعية، لكن في البرمجة يمكنك ذلك. ولكن هناك فارق بسيط. إذا حاولنا وضع in int
في short
متغير، فلن تسير الأمور بسلاسة بالنسبة لنا. بعد كل شيء، short
المتغير يحمل فقط 16 بت من المعلومات، لكنه int
يشغل 32 بت! ونتيجة لذلك، يتم تشويه القيمة التي تم تمريرها. سيعطينا المترجم خطأ (" يا صديقي، أنت تفعل شيئًا مريبًا! "). ولكن إذا أشرنا بوضوح إلى النوع الذي نحول القيمة إليه، فسوف يمضي قدمًا وينفذ العملية.
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
bigNumber = (short) bigNumber;
System.out.println(bigNumber);
}
}
وهذا بالضبط ما فعلناه في المثال أعلاه. تم تنفيذ العملية، ولكن نظرًا لأن short
المتغير لا يتسع إلا لـ 16 بايت من أصل 32 بايت، فإن القيمة النهائية مشوهة ونحصل على الرقم -27008 . تسمى هذه العملية بالتحويل الصريح أو التضييق .
أمثلة على توسيع وتضييق أنواع المراجع
الآن دعونا نتحدث عن نفس العوامل المطبقة ليس على الأنواع البدائية، ولكن على الكائنات والمتغيرات المرجعية ! كيف يعمل هذا في جافا؟ انها في الواقع بسيطة جدا. هناك كائنات لا علاقة لها. ومن المنطقي أن نفترض أنه لا يمكن تحويلهما إلى بعضهما البعض، لا بشكل صريح ولا تلقائيًا:public class Cat {
}
public class Dog {
}
public class Main {
public static void main(String[] args) {
Cat cat = new Dog(); // Error!
}
}
هنا، بالطبع، حصلنا على خطأ. لا ترتبط الفئات Cat
و Dog
ببعضها البعض، ولم نكتب "محولًا" للانتقال من فئة إلى أخرى. من المنطقي أننا لا نستطيع القيام بذلك: ليس لدى المترجم أي فكرة عن كيفية تحويل هذه الكائنات من نوع إلى آخر. إذا كانت الأشياء مرتبطة ببعضها البعض، فهذا أمر آخر! ذات صلة كيف؟ قبل كل شيء، من خلال الميراث. دعونا نحاول استخدام الميراث لإنشاء نظام صغير من الطبقات. سيكون لدينا فئة مشتركة لتمثيل الحيوانات:
public class Animal {
public void introduce() {
System.out.println("I'm Animal");
}
}
يعلم الجميع أن الحيوانات يمكن أن تكون مستأنسة (حيوانات أليفة) أو برية:
public class WildAnimal extends Animal {
public void introduce() {
System.out.println("I'm WildAnimal");
}
}
public class Pet extends Animal {
public void introduce() {
System.out.println("I'm Pet");
}
}
على سبيل المثال، خذ الأنياب - لدينا كلاب منزلية وذئاب القيوط:
public class Dog extends Pet {
public void introduce() {
System.out.println("I'm Dog");
}
}
public class Coyote extends WildAnimal {
public void introduce() {
System.out.println ("I'm Coyote");
}
}
لقد اخترنا على وجه التحديد الفصول الأساسية لتسهيل فهمها. لا نحتاج حقًا إلى أي حقول، وطريقة واحدة تكفي. لنحاول تنفيذ هذا الكود:
public class Main {
public static void main(String[] args) {
Animal animal = new Pet();
animal.introduce();
}
}
ما رأيك سيتم عرضه على وحدة التحكم؟ هل سيتم استدعاء introduce
طريقة الفصل Pet
أو الفصل؟ Animal
حاول تبرير إجابتك قبل مواصلة القراءة. وهذه هي النتيجة! أنا حيوان أليف لماذا حصلنا على ذلك؟ كل شيء بسيط. لدينا متغير أصل وكائن سليل. عن طريق الكتابة،
Animal animal = new Pet();
قمنا بتوسيع Pet
مرجع وتخصيصه لمتغير Animal
. كما هو الحال مع الأنواع البدائية، يتم توسيع الأنواع المرجعية تلقائيًا في Java. لا تحتاج إلى كتابة تعليمات برمجية إضافية لتحقيق ذلك. الآن لدينا كائن سليل تم تعيينه لمرجع أصل. نتيجة لذلك، نرى أن استدعاء الأسلوب يتم إجراؤه على الفئة التابعة. إذا كنت لا تزال لا تفهم تمامًا سبب عمل هذا الرمز، فأعد كتابته بلغة واضحة:
Animal animal = new DomesticatedAnimal();
ليس هناك مشكلة في هذا، أليس كذلك؟ تخيل أن هذه هي الحياة الحقيقية، والمرجع هو ببساطة ملصق ورقي مكتوب عليه كلمة "حيوان". إذا أخذت قطعة الورق تلك وربطتها بياقة أي حيوان أليف، فسيكون كل شيء على ما يرام. بعد كل شيء، أي حيوان أليف هو حيوان! العملية العكسية - الانتقال إلى أسفل شجرة الميراث إلى الأحفاد - تضيق:
public class Main {
public static void main(String[] args) {
WildAnimal wildAnimal = new Coyote();
Coyote coyote = (Coyote) wildAnimal;
coyote.introduce();
}
}
كما ترون، نشير هنا بوضوح إلى الفئة التي نريد تحويل الكائن إليها. كان لدينا سابقًا WildAnimal
متغير، والآن لدينا متغير Coyote
، وهو أقل في شجرة الميراث. من المنطقي أنه بدون إشارة صريحة لن يسمح المترجم بمثل هذه العملية، ولكن إذا أشرنا إلى النوع بين قوسين، فسيعمل كل شيء. فكر في مثال آخر أكثر إثارة للاهتمام:
public class Main {
public static void main(String[] args) {
Pet pet = new Animal(); // Error!
}
}
المترجم يولد خطأ! لكن لماذا؟ لأنك تحاول تعيين كائن أصل لمرجع سليل. بمعنى آخر، أنت تحاول القيام بشيء مثل هذا:
DomesticatedAnimal domesticatedAnimal = new Animal();
حسنًا، ربما سيعمل كل شيء إذا حددنا بوضوح النوع الذي نحاول التحويل إليه؟ لقد نجح ذلك مع الأرقام – فلنجربه! :)
public class Main {
public static void main(String[] args) {
Pet pet = (Pet) new Animal();
}
}
استثناء في مؤشر الترابط "الرئيسي" java.lang.ClassCastException: لا يمكن تحويل الحيوان إلى خطأ في الحيوان الأليف! لم يصرخ المترجم علينا هذه المرة، لكن انتهى بنا الأمر باستثناء. نحن نعرف السبب بالفعل: فنحن نحاول تعيين كائن أصل إلى مرجع فرعي. ولكن لماذا لا يمكنك فعل ذلك بالضبط؟ لأنه ليست كل الحيوانات حيوانات مستأنسة. لقد قمت بإنشاء Animal
كائن وتحاول تعيينه لمتغير Pet
. الذئب هو أيضًا حيوان Animal
، لكنه ليس كذلك Pet
. وبعبارة أخرى، عندما تكتب
Pet pet = (Pet) new Animal();
new Animal()
يمكن أن يمثل أي حيوان، وليس بالضرورة حيوانًا أليفًا! بطبيعة الحال، المتغير الخاص بك Pet pet
مناسب فقط لتخزين الحيوانات الأليفة (وأحفادها) وليس أي نوع من الحيوانات. لهذا السبب تم إنشاء استثناء Java خاص ClassCastException
للحالات التي يحدث فيها خطأ أثناء إرسال الفئات. دعونا نراجعه مرة أخرى لجعل الأمور أكثر وضوحا. يمكن أن يشير المرجع الأصل إلى مثيلات فئة سليل:
public class Main {
public static void main(String[] args) {
Pet pet = new Pet();
Animal animal = pet;
Pet pet2 = (Pet) animal;
pet2.introduce();
}
}
على سبيل المثال، هنا ليس لدينا أي مشاكل. لدينا Pet
كائن المشار إليه بواسطة Pet
متغير. وفي وقت لاحق، Animal
أشار مرجع إلى نفس الكائن. وبعد ذلك نقوم بالتحويل animal
إلى Pet
. بالمناسبة، لماذا نجح ذلك معنا؟ آخر مرة حصلنا على استثناء! لأن هذه المرة كائننا الأصلي هو Pet
!
Pet pet = new Pet();
لكن في المثال الأخير، كان Animal
كائنًا:
Pet pet = (Pet) new Animal();
لا يمكنك تعيين كائن أصل لمتغير سليل. يمكنك أن تفعل العكس.
المزيد من القراءة: |
---|
GO TO FULL VERSION