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