مرحبًا! اليوم سننظر في عمل آلية مهمة - الوراثة في الطبقات المتداخلة. لا أعرف إذا كنت قد فكرت يومًا فيما ستفعله عندما تحتاج إلى وراثة فئة متداخلة من فئة أخرى. إذا لم يكن الأمر كذلك، صدقوني: قد يكون هذا الوضع مربكا، لأن هناك الكثير من الفروق الدقيقة هنا:
- هل نرث فئة متداخلة من فئة ما أم نرث فئة أخرى من فئة متداخلة؟
- هل الخلف/الموروثة فئة عامة عادية أم أنها أيضًا فئة متداخلة؟
- وأخيرًا، ما هو نوع الفئات المتداخلة الذي نستخدمه بالضبط في كل هذه المواقف؟
فئات متداخلة ثابتة
قواعد الميراث الخاصة بهم هي الأبسط. هنا يمكنك أن تفعل أي شيء تقريبًا يرغب فيه قلبك. يمكن وراثة فئة متداخلة ثابتة من:- الطبقة العادية
- فئة متداخلة ثابتة تم الإعلان عنها في الطبقة الخارجية أو أسلافها
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
عامًا. إذا ظلت خاصة، فلن تتمكن الطبقة العامة العادية من الوصول إليها. لقد قمنا بفرز الطبقات الثابتة! :) الآن دعنا ننتقل إلى الطبقات الداخلية. كما تتذكر، هناك ثلاثة أنواع منها: ببساطة الطبقات الداخلية، والطبقات المحلية، والطبقات الداخلية المجهولة. مرة أخرى، دعنا ننتقل من البسيط إلى المعقد :)
فئات داخلية مجهولة
لا يمكن أن ترث فئة داخلية مجهولة من فئة أخرى. لا يمكن لأي فئة أخرى أن ترث من فئة مجهولة. لا يمكن أن يكون أبسط! :)الطبقات المحلية
يتم الإعلان عن الفئات المحلية (في حالة نسيانها) داخل كتلة التعليمات البرمجية لفئة أخرى. في أغلب الأحيان - داخل طريقة ما لهذه الطبقة الخارجية. من المنطقي أن الفئات المحلية الأخرى فقط ضمن نفس الطريقة (أو الكتلة) يمكنها أن ترث من فئة محلية. هنا مثال: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
- رقم الهاتف. إذا كنا لأغراضنا بحاجة إلى استخراج كيانين منفصلين منه، على سبيل المثال، رقم هاتف محمول ورقم هاتف أرضي، فلا يمكننا القيام بذلك إلا بنفس الطريقة. السبب بسيط: نطاق الفئة المحلية يقع ضمن الطريقة (الكتلة) التي تم الإعلان عنها. لذلك، لن نتمكن من استخدامه بطريقة أو بأخرى خارجيا (بما في ذلك الميراث). ومع ذلك، فإن الطبقة المحلية نفسها لديها إمكانيات أوسع للميراث! يمكن أن ترث الطبقة المحلية من:
- الطبقة العادية.
- فئة داخلية تم الإعلان عنها في نفس الفئة مثل الفئة المحلية أو في أسلافها.
- من فئة محلية أخرى تم الإعلان عنها بنفس الطريقة (كتلة).
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);
}
}
أوه، تبين أن المحاضرة كانت كبيرة جدًا :) لكنك تعلمت الكثير من الأشياء الجديدة! الآن هو الوقت المناسب لحل بعض المشاكل! :)
GO TO FULL VERSION