وراثت (Inheritance)
یکی از مهمترین جنبههای برنامهنویسی شیگرا وراثت است. وراثت این امکان را به یک کلاس میدهد تا اعضای یک کلاس دیگر را به ارث ببرد. با استفاده درست از قابلیت وراثت میتوان حجم کدنویسی را کم کرد و انعطافپذیری برنامه را به اندازهی قابل توجهی افزایش داد.
مثال: فرض کنید میخواهیم در برنامه خود دو کلاس به نامهای Clerk (منشی) و Accountant (حسابدار) داشته باشیم. این دو کلاس را به صورت زیر طراحی کردیم:
در کلاس حسابدار فیلدها و متدهای زیر را داریم:
فیلدها: نام، حقوق، تاریخ استخدام و میزان ساعات کاری
متدها: دریافت نام و تاریخ استخدام
public class Accountant { private String name; private double salary; private Date hireDate; private int workingHours; public String getName() { return name; } public Date getHireDate() { return hireDate; } }
در کلاس منشی:
فیلدها: نام، حقوق و تاریخ استخدام
متدها: دریافت نام و تاریخ استخدام
public class Clerk { private String name; private double salary; private Date hireDate; public String getName() { return name; } public Date getHireDate() { return hireDate; } }
همانطور که دیدید فیلدها و متدهای هر دو کلاس با هم مشترک هستند (به غیر از فیلد ساعات کاری کلاس حسابدار). ما با طراحی کلاسها با این روش موجب افزونگی کد شدیم. روش بهتر ایجاد یک کلاس جدید است که شامل این فیلدها و متدهای مشترک باشد و این دو کلاس از آن کلاس ارث بری داشته باشند.
رابطه Is-A
یکی از نکات مهم، تشخیصدادن وراثت است و اینکه چه زمان میتوان از وراثت استفاده کرد یا چه کلاسهایی میتوانند از یکدیگر ارثبری داشته باشند. اگر بین دو کلاس رابطه Is-A (هست یک) وجود داشته باشد آنگاه میتوان بین دو کلاس رابطه وراثت برقرار کرد.
مثلا حسابدار یک کارمند است. منشی یک کارمند است.
کامیون یک وسیله نقلیه است. موتورسیکلت یک وسیله نقلیه است.
البته باید توجه داشت که رابطه Is-A معمولا یکطرفه است. یعنی نمیتوان گفت کارمند یک حسابدار است چون هر کارمند الزاما یک حسابدار نیست اما هر حسابدار یک کارمند بشمار میرود.
به شکل زیر دقت کنید:
کلاسی به نام Employee (کارمند) ایجاد کردیم. کلاسهای Clerk و Accountant هر دو از کلاس Employee ارثبری خواهند داشت. منشی و حسابدار هر دو کارمند به حساب میآیند و بین این دو کلاس با کلاس Employee یک رابطه Is-A برقرار است.
کلاس Employee:
public class Employee { protected String name; protected double salary; protected Date hireDate; public String getName() { return name; } public Date getHireDate() { return hireDate; } }
کلاس Accountant:
public class Accountant extends Employee { private int workingHours; }
کلاس Clerk:
public class Clerk extends Employee { }
همانطور که میبینید اعضای مشترک دو کلاس Clerk و Accountant را حذف کرده و در کلاس Employee قرار دادیم.
تعیینکننده دسترسی فیلدهای مشترک این دو کلاس را از private به protected تغییر دادیم. دلیل این کار را به زودی خواهید فهمید.
کلاسهای Clerk و Accountant با استفاده از کلمه کلیدی extends از کلاس Employee ارثبری دارند.
بدنه کلاس Clerk خالی است چون تمام اعضایی که کلاس Clerk داشت در کلاس Employee وجود دارد اما فیلد workingHours که مخصوص کلاس Accountant بود اکنون تنها فیلد موجود در کلاس Accountant است و بقیه اعضای خود را از Employee به ارث میبرد.
نکته: به کلاسی که کلاسهای دیگر از آن ارثبری دارند Superclass و به کلاسهایی که از Superclass ارث بری دارند Subclass گفته میشود. در فارسی معمولا به Superclass کلاس پدر و به Subclass کلاس فرزند میگویند.
نکته: در جاوا یک کلاس فقط میتواند از یک کلاس ارثبری داشته باشد.
حال میخواهیم متدی به کلاس Clerk اضافه کنیم که پارامتری با عنوان tax (درصد مالیات) دریافت کرده و حقوق ماهیانه منشی را با کم کردن مالیات به ما برمیگرداند. همچنین متد دیگری به این کلاس اضافه خواهیم کرد که اطلاعات منشی (نام و میزان حقوق) را برای ما نمایش دهد:
public class Clerk extends Employee { public double getSalary(int tax) { return salary - (salary * tax / 100); } public void showInfo() { System.out.println(getName() + "-" + salary); } }
در متد getSalary از فیلد salary استفاده کردیم در صورتی که این فیلد ظاهرا در کلاس Clerk وجود ندارد اما چون این کلاس از Employee ارثبری دارد میتواند از فیلدها و متدهای آن مستقیما استفاده کند. به عبارت دیگر تمام اعضای کلاس Employee در کلاس Clerk هم وجود دارند.
در متد showInfo نیز از متد getName که در کلاس Employee وجود دارد استفاده کردیم.
حال میخواهیم در کلاسی دیگر شیئی از کلاس Clerk ایجاد کنیم و از متدهای آن استفاده کنیم. برای این کار ابتدا سازنده زیر را به کلاس Clerk اضافه میکنیم:
public Clerk(String name, Date hireDate) { this.name = name; this.hireDate = hireDate; }
سپس در کلاسی که متد main در آن قرار دارد کدهای زیر را مینویسیم:
Clerk cl = new Clerk("Ali", new Date()); cl.showInfo(); System.out.println(cl.getHireDate());
تعیینکنندههای دسترسی و وراثت
اگر در کلاسهای Clerk و Accountant میتوانستیم از فیلدها و متدهای کلاسهای Employee استفاده کنیم به این دلیل بود آن فیلدها و متدها به صورت protected و public تعریف شده بودند.
جدول زیر از قسمت تعیین کنندههای دسترسی را به یاد میآورید:
کل برنامه |
کلاسهای وارث |
پکیج |
داخل کلاس |
تعیین کننده دسترسی |
* |
* |
* |
* |
public |
* |
* |
package-private |
||
* |
private |
|||
* |
* |
* |
protected |
همانطور که در جدول مشخص است اگر اعضای یک کلاس پدر از نوع package-private یا private باشند نمیتوان از آنها در کلاسهای فرزند آن استفاده کرد و از دید کلاس فرزند مخفی هستند. البته این به این معنی نیست که این اعضای مخفی در اشیا ساخته شده از کلاس فرزند وجود ندارند فقط فرق آنها با اعضای public و protected این است که نمیتوان در کلاس فرزند به آنها دسترسی داشت.
اگر دو کلاس با نامهای A و B داشته باشیم که کلاس A کلاس پدر کلاس B باشد:
public class A { private int id; public int getId() { return id; } }
public class B extends A { }
چون فیلد int در کلاس اول private است پس نمیتوان در کلاس B با نوشتن نام فیلد id به آن دسترسی داشت. اما اگر شیئی از کلاس B بسازیم میتوانیم با فراخوانی متد getId مقدار آن را بگیریم. پس فیلد id اگرچه در کدنویسی از دید کلاس B مخفی است اما در اشیا ساخته شده از آن به صورت ضمنی وجود دارد.
بنظر من یکی از تفاوت های برنامه نویس حرفه ای و معمولی در استفاده از همین نکات و بهینه کردن کدهای نوشته شده است.
با سلام و خسته نباشید
در کد اول این صفحه منظور از :
private Date hireDate
چیه؟
hireDate متغیری از جنس متد Date هستش؟
با سلام
خیر Date متد نیست. Date یک کلاس است و از طریق اشیا ساخته شده از این کلاس می توان به اطلاعات تاریخ و زمان دسترسی داشت. در این کد متغیری از جنس کلاس Date به نام hireDate نوشتیم که به این متغیر می توان یک شی ساخته شده از کلاس Date را نسبت داد. اگر مطلب را تا آخر مطالعه کنید متوجه خواهید شد.
موفق باشید
ضمن تشکر
پس چرا موقع تعریف Date قبلش class نذاشتیم؟
مگر اینکه از Composition استفاده کرده باشیم یعنی قبلش Date تعریف شده
درسته؟
بله دقیقا. ببینید کلاس Date در پکیج java.util قرار داره که برای کار با تاریخ و زمان به کار میره. کوتاهی از من بود که به این نکته در ابتدا اشاره نکردم. برای استفاده از این کلاس باید این کلاس رو به صورت زیر import کنید:
import java.util.Date;
خیلی ممنون
امیدوارم ادامه بدین
خیلی استفاده بردم تا حالا
سلام
مرسی از اموزش خوبتون
فقط من یه چیزی رو متوجه نشدم :
در بخش محاسبه حقوق منشی داخل متد showInfo اومدین از متد getName استفاده کردین در صورتی فیلد Name از کلاس Employee ارث بری شده و ما میتونیم به طور مستقیم از این فیلد در این کلاس استفاده کنیم ( البته اگه اشتباه نکرده باشم !!!)
چرا شما مستقیما از این فیلد استفاده نکردین ( در صورتی که در این کدها کلاسی وجود نداره که دسترسی به فیلد Name نداشته باشه و بخایم از متد استفاده کنیم ) ؟؟؟
درسته چون دسترسی فیلد name به صورت protected تعریف شده میتونستیم به جای فراخوانی متد getName مستقیما از خود فیلد name استفاده کنیم و تنها دلیل اینکه متد getName را فراخوانی کردیم برای نشون دادن این بود که در کلاس های فرزند میشه از متدهای کلاس پدر استفاده کرد.
اما توجه داشته باشید که اگر تعیین کننده دسترسی فیلد name در کلاس Employee برابر با private بود اونوقت نمی تونستیم مستقیما به فیلد دسترسی داشته باشیم و حتما باید با فراخوانی متد getName مقدار name را به دست می آوردیم.
موفق باشید
مرسی از توضیحتون . یه پیشنهاد ه داشتم :
کاش بعد از هر بخش چن تا تمرین هم میذاشتین حالابا جواب یا بدون جواب چون واقعا بدون تمرین برنامه نویسی هیچی نیس و اگه بخایم هم داخل کتابا دنبال تمرین باشیم ممکنه سرفصل های اونا کمتر یا بیشتر از توضیح شما باشه و شاید زیاد جالب نباشه
به هر حال مطالبتون عالیه و جای تقدیر داره
موفق باشید
چشم از این به بعد سعی می کنم در آخر هر قسمت تمرین بزارم.
با تشکر از توجه شما به مطالب
با سلام ، از آموزش کامل و جامعتون واقعا ممنونم . من تازه برنامه نویسی جاوا را شروع کردم و بیشتر روی سرویس ها میخوام یاد بگیرم . تا اونجایی که میدونم در سرویس ها نمی توانیم فیلدی را تعریف کنیم اگر من قرار باشه از interface استفاده کنم چه جوری میتونم از این کدها استفاده کنم ؟ به عنوان مثال بخوام کارمندانی که حقوق ثابت میگیرن ، کارمندانی که ساعتی حقوق میگیرند را چگونه می تونم در قالب سرویس براشون متد بنویسم ؟ ممنون میشم اگر منو راهنمایی کنید .
سلام. منظورتون از سرویس رو متوجه نشدم لطفا واضح تر بگید. درباره اینترفیس ها هم یک مطلب کامل در همین مجموعه آموزشی وجود داره میتونید استفاده کنید.
سلام خسته نباشید
در قسمت ساخت شی از کلاس clerk
تو پارامتر دوم که متغیر از جنس Date بود چرا new Date گذاشتید؟