JavaRush /مدونة جافا /Random-AR /توسيع وتقليص الأنواع المرجعية

توسيع وتقليص الأنواع المرجعية

نشرت في المجموعة
مرحبًا! ناقشنا في إحدى المحاضرات السابقة صب الأنواع البدائية. دعونا نتذكر بإيجاز ما كنا نتحدث عنه. توسيع وتقليص الأنواع المرجعية - 1قمنا بتمثيل الأنواع البدائية (في هذه الحالة، رقمية) كدمى متداخلة وفقًا لحجم الذاكرة التي تشغلها. كما تتذكر، فإن وضع دمية صغيرة في دمية أكبر سيكون أمرًا بسيطًا سواء في الحياة الواقعية أو في برمجة Java.
public class Main {
   public static void main(String[] args) {
        short smallNumber = 100;
        int bigNumber =  smallNumber;
        System.out.println(bigNumber);
   }
}
هذا مثال على التحويل التلقائي، أو الامتداد . يحدث ذلك من تلقاء نفسه، لذلك ليست هناك حاجة لكتابة تعليمات برمجية إضافية. في النهاية، نحن لا نفعل أي شيء غير عادي: نحن ببساطة نضع دمية تعشيش أصغر في دمية تعشيش أكبر. إنها مسألة أخرى إذا حاولنا أن نفعل العكس ونضع دمية ماتريوشكا كبيرة في دمية أصغر. لا يمكن القيام بذلك في الحياة، ولكن في البرمجة يمكن القيام به. ولكن هناك تحذير واحد. إذا حاولنا وضع قيمة intفي متغير short، فلن ينجح الأمر بهذه السهولة. بعد كل شيء، يمكن استيعاب 16 بت فقط من المعلومات في المتغير short، لكن القيمة intتستغرق 32 بت! ونتيجة لذلك، سيتم تشويه القيمة المرسلة. سيعطينا المترجم خطأ (" يا صديقي، أنت تفعل شيئًا مريبًا! ")، ولكن إذا حددنا بوضوح النوع الذي نلقي عليه القيمة، فسيستمر في تنفيذ مثل هذه العملية.
public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
في المثال أعلاه، فعلنا ذلك بالضبط. اكتملت العملية، ولكن بما أن short16 بت فقط من أصل 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 تلقائيًا. ليست هناك حاجة لكتابة رمز إضافي لهذا الغرض. الآن لدينا كائن فرعي مرتبط بالمرجع الأصلي، ونتيجة لذلك نرى أنه يتم استدعاء الطريقة في الفصل الفرعي. إذا كنت لا تزال لا تفهم تمامًا سبب عمل هذا الرمز، فأعد كتابته بلغة بسيطة:
Животное животное = new ДомашнееЖивотное();
ليس هناك مشكلة في ذلك، أليس كذلك؟ تخيل أن هذه هي الحياة الحقيقية، والرابط في هذه الحالة عبارة عن بطاقة ورقية بسيطة مكتوب عليها "حيوان". إذا أخذت مثل هذه القطعة من الورق وأرفقتها بياقة أي حيوان أليف، فسيكون كل شيء على ما يرام. أي حيوان أليف لا يزال حيوانا! أما العملية العكسية، أي الانتقال إلى أسفل شجرة الميراث إلى الورثة، فهي تضييق:
public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
كما ترون، نشير هنا بوضوح إلى الفئة التي نريد إرسال كائننا إليها. في السابق كان لدينا متغير WildAnimal، والآن Coyote، والذي يذهب إلى أسفل شجرة الميراث. من المنطقي أن المترجم لن يتخطى مثل هذه العملية دون إشارة صريحة، ولكن إذا قمت بتحديد النوع بين قوسين، فسيعمل كل شيء. توسيع وتقليص الأنواع المرجعية - 2 دعونا نلقي نظرة على مثال آخر، أكثر إثارة للاهتمام:
public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal();//error!
   }
}
المترجم يلقي خطأ! ماهو السبب؟ الحقيقة هي أنك تحاول تعيين كائن أصل لمتغير فرعي. بمعنى آخر، تريد القيام بذلك:
ДомашнееЖивотное домашнееЖивотное = new Животное();
لكن ربما لو أشرنا بوضوح إلى النوع الذي نحاول الإدلاء به سننجح؟ يبدو أن الأرقام ناجحة، فلنجربها! :)
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 pet =  new Pet();
وفي المثال السابق كان كائنًا Animal:
Pet pet = (Pet) new Animal();
لا يمكن تعيين متغير سليل لكائن أصل. على العكس من ذلك، يمكنك أن تفعل ذلك.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION