سلام! امروز یک مکانیسم مهم را بررسی خواهیم کرد: وراثت در کلاسهای تودرتو. آیا تا به حال به این فکر کردهاید که اگر بخواهید یک کلاس تودرتو را به ارث بردن کلاس دیگری تبدیل کنید، چه کاری انجام میدهید. اگر نه، باور کنید: این وضعیت می تواند گیج کننده باشد، زیرا تفاوت های ظریف زیادی وجود دارد.
قوانین وراثت آنها ساده ترین است. در اینجا تقریباً می توانید هر کاری که دلتان بخواهد انجام دهید. یک کلاس تودرتو ایستا می تواند ارث بری داشته باشد:
دوباره بیایید از ساده به پیچیده برویم :)
- آیا ما یک کلاس تودرتو می سازیم که کلاسی را به ارث ببرد؟ یا اینکه برخی از کلاس ها را به ارث بردن یک کلاس تودرتو می سازیم؟
- آیا کلاس فرزند/والد یک کلاس عمومی معمولی است یا یک کلاس تودرتو نیز هست؟
- در نهایت، ما از چه نوع کلاس های تودرتو در همه این موقعیت ها استفاده می کنیم؟
کلاس های تو در تو استاتیک

- یک کلاس معمولی
- یک کلاس تودرتو ایستا که در یک کلاس خارجی یا اجداد آن اعلام شده است
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 کلاس محلی خود را به ارث ببریم. مثال 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);
}
}
اوه! این درس نسبتا طولانی بود :) اما شما چیزهای زیادی یاد گرفتید! اکنون زمان حل برخی از کارها است! :)
GO TO FULL VERSION