CodeGym /وبلاگ جاوا /Random-FA /گسترش و باریک شدن انواع مرجع
John Squirrels
مرحله
San Francisco

گسترش و باریک شدن انواع مرجع

در گروه منتشر شد
سلام! در یک درس گذشته، ما در مورد ریخته گری انواع اولیه بحث کردیم. بیایید به طور خلاصه آنچه را که مورد بحث قرار گرفت، یادآوری کنیم. گسترش و باریک شدن انواع مرجع - 1ما انواع ابتدایی (در این مورد، انواع عددی) را به عنوان عروسک‌های تودرتو تصور می‌کردیم که اندازه آنها بسته به میزان حافظه آنها متفاوت است. همانطور که به یاد دارید، قرار دادن یک عروسک کوچکتر در یک عروسک بزرگتر هم در زندگی واقعی و هم در برنامه نویسی جاوا ساده است.
public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
این نمونه ای از تبدیل یا گسترش خودکار است . این به خودی خود اتفاق می افتد، بنابراین نیازی به نوشتن کد اضافی ندارید. در پایان، ما هیچ کار غیرعادی انجام نمی دهیم: ما فقط یک عروسک کوچکتر را در یک عروسک بزرگتر قرار می دهیم. اگر سعی کنیم برعکس عمل کنیم و یک عروسک روسی بزرگتر را در یک عروسک کوچکتر قرار دهیم، موضوع دیگری است. در زندگی واقعی نمی توانید این کار را انجام دهید، اما در برنامه نویسی می توانید. اما یک نکته ظریف وجود دارد. اگر بخواهیم an را intدر یک shortمتغیر قرار دهیم، همه چیز برای ما راحت پیش نمی رود. به هر حال، shortمتغیر فقط 16 بیت اطلاعات را در خود جای می دهد، اما یک متغیر int32 بیت را اشغال می کند! در نتیجه مقدار ارسال شده مخدوش می شود. کامپایلر به ما یک خطا می دهد (' عزیزم، تو کار مشکوکی انجام می دهی! '). اما اگر به صراحت نوع موردی را که مقدار خود را به آن تبدیل می‌کنیم مشخص کنیم، این کار ادامه می‌یابد و عملیات را انجام می‌دهد.
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متغیر اختصاص دادیم. مانند انواع اولیه، انواع مرجع به طور خودکار در جاوا گسترده می شوند. برای انجام آن نیازی به نوشتن کد اضافی ندارید. اکنون یک شی زاده داریم که به یک مرجع والد اختصاص داده شده است. در نتیجه می بینیم که فراخوانی متد بر روی کلاس descendant انجام می شود. اگر هنوز به طور کامل نمی‌دانید چرا این کد کار می‌کند، آن را به زبان ساده بازنویسی کنید:
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که در درخت وراثت پایین‌تر است. منطقی است که بدون علامت صریح، کامپایلر اجازه چنین عملیاتی را نمی دهد، اما اگر نوع را در پرانتز نشان دهیم، همه چیز کار می کند. تعریض و باریک شدن انواع مرجع - 2مثال جالب تر دیگری را در نظر بگیرید:
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فقط برای نگهداری حیوانات خانگی (و فرزندان آنها) مناسب است و نه هر نوع حیوانی. به همین دلیل است که یک استثنای خاص جاوا، 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به a تبدیل می کنیم 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