JavaRush /مدونة جافا /Random-AR /وراثة الطبقات المتداخلة

وراثة الطبقات المتداخلة

نشرت في المجموعة
مرحبًا! اليوم سننظر في عمل آلية مهمة - الوراثة في الطبقات المتداخلة. لا أعرف إذا كنت قد فكرت يومًا فيما ستفعله عندما تحتاج إلى وراثة فئة متداخلة من فئة أخرى. إذا لم يكن الأمر كذلك، صدقوني: قد يكون هذا الوضع مربكا، لأن هناك الكثير من الفروق الدقيقة هنا:
  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مرة أخرى، دعنا ننتقل من البسيط إلى المعقد :)

فئات داخلية مجهولة

لا يمكن أن ترث فئة داخلية مجهولة من فئة أخرى. لا يمكن لأي فئة أخرى أن ترث من فئة مجهولة. لا يمكن أن يكون أبسط! :)

الطبقات المحلية

يتم الإعلان عن الفئات المحلية (في حالة نسيانها) داخل كتلة التعليمات البرمجية لفئة أخرى. في أغلب الأحيان - داخل طريقة ما لهذه الطبقة الخارجية. من المنطقي أن الفئات المحلية الأخرى فقط ضمن نفس الطريقة (أو الكتلة) يمكنها أن ترث من فئة محلية. هنا مثال:
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 {


       }

       //...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);
           }
       }

       //...code валидации номера
   }
}
هنا قمنا بإخراج الفصل PhoneNumberمن الطريقة validatePhoneNumber()وجعلناه داخليًا بدلاً من المحلي. هذا لا يمنعنا من وراثة فئتين محليتين منه. مثال 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);
           }
       }

       //...code валидации номера
   }
}
بفضل التواصل من خلال الميراث، فإن الطبقات المحلية داخل الطبقة المنحدرة "ترى" الطبقات الداخلية داخل السلف. وأخيرا، دعونا ننتقل إلى المجموعة الأخيرة :)

الطبقات الداخلية

يمكن وراثة الطبقة الداخلية بواسطة فئة داخلية أخرى معلنة في نفس الفئة الخارجية (أو سليلها). دعونا نلقي نظرة على هذا باستخدام مثال الدراجة الخاص بنا من المحاضرة حول الطبقات الداخلية.
public class Bicycle {

   private String model;
   private int mawWeight;

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

   public void start() {
       System.out.println("Go!");
   }

   class Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }

   class SportSeat extends Seat {

       //...methods
   }
}
Bicycleلقد أعلنا هنا عن فصل داخلي داخل الفصل Seat- المقعد. تم توريث نوع فرعي خاص من مقاعد السباق منه - SportSeat. ومع ذلك، يمكننا إنشاء نوع منفصل من "دراجات السباق" ووضعه في فئة منفصلة:
public class SportBicycle extends Bicycle {

   public SportBicycle(String model, int mawWeight) {
       super(model, mawWeight);
   }


   class SportSeat extends Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }
}
وهذا ممكن أيضا. الطبقة الداخلية للطفل ( SportBicycle.SportSeat) "ترى" الطبقات الداخلية للسلف ويمكن أن ترث منها. الوراثة من الطبقات الداخلية لها ميزة واحدة مهمة جدًا! في المثالين السابقين SportSeatكان لدينا داخلي. ولكن ماذا لو قررنا أن نجعلها SportSeatطبقة عامة عادية ترث أيضًا من الطبقة الداخلية Seat؟
//ошибка! No inclosing instance of  type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
لقد حصلنا على خطأ! هل يمكنك تخمين ما هو متصل به؟ :) انه سهل. عندما تحدثنا عن الطبقة الداخلية Bicycle.Seat، ذكرنا أن منشئ الطبقة الداخلية يمرر ضمنيًا إشارة إلى كائن من الطبقة الخارجية. لذلك، بدون إنشاء كائن، Bicycleلا يمكنك إنشاء كائن Seat. ماذا عن الخلق SportSeat؟ لا تحتوي على نفس الآلية المضمنة لتمرير مرجع ضمني إلى كائن فئة خارجية في المنشئ كما في Seat. ومع ذلك، بدون كائن Bicycle، تمامًا كما في حالة Seat، لا يمكننا إنشاء كائن SportSeat. لذلك، لم يتبق لدينا سوى شيء واحد للقيام به - وهو تمرير SportSeatإشارة إلى الكائن Bicycleبشكل صريح إلى المنشئ. وإليك كيف يتم ذلك:
class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
لهذا نستخدم كلمة خاصة super(); الآن، إذا أردنا إنشاء كائن 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