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

نمونه هایی از وراثت کلاس های تو در تو

در گروه منتشر شد
سلام! امروز یک مکانیسم مهم را بررسی خواهیم کرد: وراثت در کلاس‌های تودرتو. آیا تا به حال به این فکر کرده‌اید که اگر بخواهید یک کلاس تودرتو را به ارث بردن کلاس دیگری تبدیل کنید، چه کاری انجام می‌دهید. اگر نه، باور کنید: این وضعیت می تواند گیج کننده باشد، زیرا تفاوت های ظریف زیادی وجود دارد.
  1. آیا ما یک کلاس تودرتو می سازیم که کلاسی را به ارث ببرد؟ یا اینکه برخی از کلاس ها را به ارث بردن یک کلاس تودرتو می سازیم؟
  2. آیا کلاس فرزند/والد یک کلاس عمومی معمولی است یا یک کلاس تودرتو نیز هست؟
  3. در نهایت، ما از چه نوع کلاس های تودرتو در همه این موقعیت ها استفاده می کنیم؟
پاسخ‌های احتمالی زیادی برای همه این سؤالات وجود دارد، سرتان می‌چرخد :) همانطور که می‌دانید، می‌توانیم یک مسئله پیچیده را با تقسیم کردن آن به بخش‌های ساده‌تر حل کنیم. بیا این کار را انجا دهیم. اجازه دهید هر گروه از کلاس های تودرتو را به نوبه خود از دو منظر در نظر بگیریم: چه کسی می تواند هر نوع کلاس تودرتو را به ارث ببرد و چه کسی می تواند به ارث ببرد. بیایید با کلاس های تودرتو ایستا شروع کنیم.

کلاس های تو در تو استاتیک

نمونه هایی از وراثت کلاس های تو در تو - 2قوانین وراثت آنها ساده ترین است. در اینجا تقریباً می توانید هر کاری که دلتان بخواهد انجام دهید. یک کلاس تودرتو ایستا می تواند ارث بری داشته باشد:
  • یک کلاس معمولی
  • یک کلاس تودرتو ایستا که در یک کلاس خارجی یا اجداد آن اعلام شده است
نمونه ای از درس ما در مورد کلاس های تودرتو ایستا را به یاد بیاورید.

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
       public static int getMaxPassengersCount() {
          
           return maxPassengersCount;
       }
   }
}
بیایید سعی کنیم کد را تغییر دهیم و یک Drawingکلاس تودرتو ایستا و نسل آن ایجاد کنیم - Boeing737Drawing.

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
   }
  
   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
همانطور که می بینید مشکلی نیست. ما حتی می‌توانیم کلاس را بیرون بکشیم Drawingو آن را به‌جای یک کلاس تودرتو ایستا، به یک کلاس عمومی معمولی تبدیل کنیم - هیچ چیز تغییر نخواهد کرد.

public class Drawing {
  
}

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
ما این را درک می کنیم. اما چه کلاس هایی می توانند یک کلاس تودرتو ایستا را به ارث ببرند؟ عملا هر! تودرتو/غیر تودرتو، ایستا/غیر ساکن - مهم نیست. در اینجا ما Boeing737Drawingکلاس داخلی را به ارث می بریم Drawingکلاس تودرتو ایستا:

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
   }

   public class Boeing737Drawing extends Drawing {

       public int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
شما می توانید یک نمونه از Boeing737Drawingاین قبیل ایجاد کنید:

public class Main {

   public static void main(String[] args) {

      Boeing737 boeing737 = new Boeing737(1990);
      Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
      System.out.println(drawing.getMaxPassengersCount());

   }

}
اگرچه کلاس ما Boeing737Drawingیک کلاس استاتیک را به ارث می برد، اما خود ایستا نیست! در نتیجه، همیشه به یک نمونه از کلاس بیرونی نیاز خواهد داشت. ما می توانیم Boeing737Drawingکلاس را از Boeing737کلاس حذف کنیم و آن را به یک کلاس عمومی ساده تبدیل کنیم. هیچ چیز تغییر نمی کند. همچنان می تواند Drawingکلاس تودرتو ایستا را به ارث ببرد.

public class Boeing737 {

   private int manufactureYear;
   public static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }
}

public class Boeing737Drawing extends Boeing737.Drawing {

   public int getMaxPassengersCount() {

       return Boeing737.maxPassengersCount;
   
}
تنها نکته مهم این است که در این مورد باید maxPassengersCountمتغیر استاتیک را عمومی کنیم. اگر خصوصی بماند، یک کلاس عمومی معمولی به آن دسترسی نخواهد داشت. ما کلاس های استاتیک را کشف کرده ایم! :) حالا بریم سراغ کلاس های داخلی. آنها در 3 نوع هستند: کلاس های داخلی ساده، کلاس های محلی، و کلاس های داخلی ناشناس. نمونه هایی از وراثت کلاس های تو در تو - 3دوباره بیایید از ساده به پیچیده برویم :)

کلاس های داخلی ناشناس

یک کلاس داخلی ناشناس نمی تواند کلاس دیگری را به ارث ببرد. هیچ کلاس دیگری نمی تواند یک کلاس ناشناس را به ارث ببرد. ساده تر از این نمی توانست باشد! :)

کلاس های محلی

کلاس های محلی (در صورتی که فراموش کرده باشید) در داخل یک بلوک کد کلاس دیگر اعلام می شوند. اغلب، این در داخل برخی از روش های کلاس خارجی اتفاق می افتد. به طور منطقی، فقط کلاس های محلی دیگر در داخل همان متد (یا بلوک کد) می توانند یک کلاس محلی را به ارث ببرند. به عنوان مثال:

public class PhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       class CellPhoneNumber extends PhoneNumber {

       }

       class LandlinePhoneNumber extends PhoneNumber {
          
          
       }

       // ...number validation code
   }
}
این کد از درس ما در کلاس های محلی است. کلاس اعتبارسنجی شماره ما یک PhoneNumberکلاس محلی دارد. اگر به آن برای نشان دادن دو موجودیت مجزا نیاز داشته باشیم، به عنوان مثال، یک شماره تلفن همراه و یک شماره تلفن ثابت، فقط می توانیم این کار را در همان روش انجام دهیم. دلیل ساده است: محدوده یک کلاس محلی به متدی (بلوک کد) که در آن اعلام شده است محدود می شود. در نتیجه، ما نمی توانیم از آن به صورت خارجی (از جمله برای ارث بری کلاس) استفاده کنیم. با این حال، امکانات وراثت در خود کلاس محلی بسیار گسترده تر است! یک کلاس محلی می تواند به ارث ببرد:
  1. یه کلاس معمولی
  2. یک طبقه درونی که در همان کلاس کلاس محلی یا در یکی از اجداد آن اعلام شده است.
  3. کلاس محلی دیگری که در همان متد اعلام شده است (بلوک کد).
نکته اول و سوم بدیهی به نظر می رسد، اما دومی کمی گیج کننده است:/ بیایید به دو مثال نگاه کنیم. مثال 1 - "ساخت یک کلاس محلی به ارث بردن یک کلاس داخلی که در همان کلاس کلاس محلی اعلام شده است":

public class PhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
PhoneNumberدر اینجا کلاس را از متد حذف کردیم validatePhoneNumber()و به جای کلاس محلی، آن را به یک کلاس داخلی تبدیل کردیم. این مانع ما نمی شود که 2 کلاس محلی خود را به ارث ببریم. مثال 2 - "... یا در اجداد این طبقه." در حال حاضر این در حال حاضر جالب تر است. ما می توانیم PhoneNumberدر زنجیره ارث حتی بالاتر حرکت کنیم. بیایید یک AbstractPhoneNumberValidatorکلاس انتزاعی را اعلام کنیم، که جد PhoneNumberValidatorکلاس ما خواهد شد:

public abstract class AbstractPhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

}
همانطور که می بینید، ما فقط آن را اعلام نکردیم - ما همچنین PhoneNumberکلاس داخلی را به آن منتقل کردیم. با این حال، در نسل خود PhoneNumberValidator، کلاس های محلی اعلام شده در متدها می توانند PhoneNumberبدون هیچ مشکلی ارث بری کنند!

public class PhoneNumberValidator extends AbstractPhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
با توجه به رابطه وراثت، طبقات محلی در داخل یک طبقه نسل، طبقات درونی را در داخل یک اجداد «می بینند». و در آخر بریم سراغ آخرین گروه :)

کلاس های داخلی

یک کلاس داخلی اعلام شده در همان کلاس خارجی (یا در نسل آن) می تواند کلاس داخلی دیگری را به ارث ببرد. بیایید این را با استفاده از مثال خود با دوچرخه از درس کلاس های داخلی بررسی کنیم.

public class Bicycle {

   private String model;
   private int maxWeight;

   public Bicycle(String model, int maxWeight) {
       this.model = model;
       this.maxWeight = maxWeight;
   }

   public void start() {
       System.out.println("Let's go!");
   }

   class Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }

   class SportSeat extends Seat {
      
       // ...methods
   }
}
Seatدر اینجا کلاس داخلی را در داخل کلاس اعلام کردیم Bicycle. نوع خاصی از صندلی مسابقه، SportSeatآن را به ارث می برد. اما، می‌توانیم یک نوع «دوچرخه مسابقه» جداگانه ایجاد کنیم و آن را در یک کلاس جداگانه قرار دهیم:

public class SportBicycle extends Bicycle {
  
   public SportBicycle(String model, int maxWeight) {
       super(model, maxWeight);
   }

  
   class SportSeat extends Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
این نیز یک گزینه است. طبقه باطن ذریه ( SportBicycle.SportSeat) طبقات درونی جد را «می بیند» و می تواند آنها را به ارث ببرد. ارث بردن کلاس های داخلی یک ویژگی بسیار مهم دارد! در دو مثال قبلی SportSeatکلاس ما یک کلاس درونی بود. SportSeatاما اگر تصمیم بگیریم یک طبقه عمومی معمولی بسازیم که به طور همزمان Seatطبقه درونی را به ارث می برد، چه؟

// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
ما یک خطا دریافت کردیم! می توانید حدس بزنید چرا؟ :) همه چیز سرراست است. وقتی در مورد کلاس داخلی صحبت کردیم Bicycle.Seat، اشاره کردیم که ارجاع به نمونه ای از کلاس خارجی به طور ضمنی به سازنده کلاس داخلی منتقل می شود. این بدان معنی است که شما نمی توانید یک Seatشی را بدون ایجاد یک Bicycleشی ایجاد کنید. اما در مورد ایجاد یک SportSeat؟ بر خلاف Seat, این مکانیزم داخلی برای ارسال ضمنی سازنده ارجاع به نمونه ای از کلاس خارجی را ندارد. تا زمانی که، بدون یک Bicycleشی، ما نمی توانیم یک SportSeatشی ایجاد کنیم، همانطور که در مورد Seat. بنابراین، تنها یک کار برای ما باقی مانده است - به طور صریح SportSeatارجاع به یک Bicycleشی را به سازنده منتقل کنیم. در اینجا نحوه انجام آن آمده است:

class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
سازنده superclass را با استفاده از super(); Now فراخوانی می کنیم، اگر بخواهیم یک SportSeatشی ایجاد کنیم، هیچ چیز ما را از انجام این کار باز نمی دارد:

public class Main {

   public static void main(String[] args) {

       Bicycle bicycle = new Bicycle("Peugeot", 120);
       SportSeat peugeotSportSeat = new SportSeat(bicycle);

   }
}
اوه! این درس نسبتا طولانی بود :) اما شما چیزهای زیادی یاد گرفتید! اکنون زمان حل برخی از کارها است! :)
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION