بررسی مفهوم وراثت در قالب پروژۀ ارث‌بری فرزندان از خصوصیات پدر در زبان جاوا


پس از آشنایی با مفهوم وراثت در زبان برنامه‌نویسی جاوا در آموزش گذشته، در این آموزش قصد داریم تا سناریویی همچون مثال مطرح‌شده در آموزش قبل را مد نظر قرار داده و آن را پیاده‌سازی نماییم که برای این منظور پروژه‌ای تحت عنوان InheritanceProject ایجاد کرده و داخل آن سه کلاس مجزا تحت عناوین Grandfather ،Father و Son به ترتیب برای تعریف خصوصیات پدربزرگ، پدر و پسر ایجاد می‌کنیم (نکتۀ قابل‌توجه در ارتباط با این کلاس‌ها این است که برای ساخت آن‌ها نیازی به زدن تیک گزینۀ public static void main نداریم چرا که کلاس‌های مذکور به منزلۀ نقطه شروع برنامه نمی‌باشند.)

در ادامه، کلاسی تحت عنوان ActionClass ایجاد کرده و در زمان ساخت آن نیز تیک گزینۀ public static void main را می‌زنیم و بدین ترتیب کلاس ActionClass را به عنوان کلاسی برای نقطۀ شروع اجرای برنامۀ خود قرار می‌دهیم. حال برای شروع کدنویسی پروژه از کلاس Grandfather شروع می‌کنیم که پس از باز کردن فایل این کلاس کدی به صورت زیر خواهیم داشت:

public class Grandfather {

}

اکنون در کلاس Grandfather به منظور ذخیره‌سازی خصوصیات پدربزرگ، یکسری متد تعریف می‌کنیم و برای این منظور کلاس فوق را به صورت زیر تکمیل می‌نماییم:

public class Grandfather {
    public void showGrandfatherHeight() {
        String height = "Short";
        System.out.println(height);
    }
    public void showGrandfatherSkinColor() {
        String skinColor = "Bright";
        System.out.println(skinColor);
    }
    public void showGrandfatherBaldness() {
        String baldness = "Bald";
        System.out.println(baldness);
    }
    public void showGrandfatherBehavior() {
        String behavior = "Angry";
        System.out.println(behavior);
    }
    public void showGrandfatherCreativity() {
        String creativity = "Very Creative";
        System.out.println(creativity);
    }
    public void showGrandfatherActivity() {
        String activity = "Active";
        System.out.println(activity);
    }
    public void showGrandfatherNationality() {
        String nationality = "Iranian";
        System.out.println(nationality);
    }
}

همان‌طور که مشاهده می‌کنیم، سطوح دسترسی تمامی متدهای کلاس را public در نظر گرفته‌ایم چرا که می‌خواهیم از طریق سایر کلاس‌ها در دسترس باشند (در آموزش‌ آشنایی با انواع سطوح دسترسی در زبان برنامه‌نویسی جاوا سطوح مختلف دسترسی را در این زبان مورد بررسی قرار داده‌ايم.)

در كد فوق، ابتدا متدی تحت عنوان ()showGrandfatherHeight تعریف کرده‌ایم و دستورات داخلی آن را به منظور نمایش قد پدربزرگ بدین صورت پیاده‌سازی کرده‌ایم که در آن گفته‌ایم در صورت فراخوانی متد مذکور، آبجکتی از روی کلاس String تحت عنوان height ساخته شده و استرینگ «Short» بدان منتسب شود و در ادامه height به عنوان آرگومان ورودی به متد ()println پاس داده شده تا استرینگ اختصاص‌یافته به این متغیر در کنسول چاپ گردد.

در ادامه، متدی تحت عنوان ()showGrandfatherSkinColor تعریف کرده‌ایم که دستورات داخلی آن به منظور نمایش رنگ پوست پدربزرگ بدین صورت تعریف شده‌اند که در صورت فراخوانی متد مذکور، آبجکتی به نام skinColor از روی کلاس String ساخته شده و استرینگ «Bright» به آن اختصاص داده می‌شود و در ادامه مشابه متد قبل، استرینگ مذکور در کنسول چاپ می‌گردد.

متد سوم نیز تحت عنوان ()showGrandfatherBaldness تعریف شده و دستورات داخلی آن به منظور نشان دادن طاسی پدربزرگ بدین صورت پیاده‌سازی شده‌اند که در صورت فراخونی این متد، آبجکتی به نام baldness از روی کلاس String ساخته شده و استرینگ «Bald» بدان منتسب شده و در ادامه مشابه متدهای پیشین، استرینگ مذکور در کنسول نمایش داده می‌شود.

در ادامه، متد دیگری تحت عنوان ()showGrandfatherBehavior تعریف کرده‌ایم که در صورت فراخوانی، آبجکتی به نام behavior از روی کلاس String ساخته شده و استرینگ «Angry» بدان منتسب می‌شود و در ادامه با به‌کارگیری متد ()println مقدار منتسب به آن در کنسول چاپ می‌گردد.

همچنین متد دیگری تحت عنوان ()showGrandfatherCreativity داریم که به منظور نمایش میزان خلاقیت پدربزرگ تعریف شده و در صورت فراخوانی متد مذکور، آبجکتی به نام creativity از روی کلاس String ساخته شده و استرینگ «Very Creative» بدان منتسب می‌گردد و در ادامه مشابه متدهای پیشین، مقدار اختصاص‌یافته به creativity در کنسول چاپ می‌شود.

در ادامه، به منظور نمایش میزان فعالیت پدربزرگ، متد دیگری به نام ()showGrandfatherActivity تعریف کرده و دستورات داخلی آن را بدین صورت تعریف کرده‌ایم تا در صورت فراخوانی، آبجکتی به نام Activity از روی کلاس String ساخته و استرینگ «Active» را بدان منتسب کرده و در ادامه مشابه آنچه در متدهای پیشین توضیح دادیم، استرینگ مذکور را در کنسول چاپ می‌کند.

در نهایت متد هفتم تحت عنوان ()showGrandfatherNationality را تعریف کرده‌ایم که به منظور نمایش ملیت پدربزرگ به کار گرفته شده و در حین فراخوانی، ابتدا آبجکتی از روی کلاس String تحت عنوان nationality ساخته و استرینگ «Iranian» را بدان اختصاص داده و در ادامه با فراخوانی متد ()println استرینگ مذکور را در کنسول چاپ می‌نماید.

اکنون به منظور تست برنامۀ فوق، آبجکتی از روی کلاس Grandfather در کلاس ActionClass ایجاد کرده سپس متدهای تعریف‌شده در کلاس Grandfather را روی آبجکت ساخته‌شده فراخوانی می‌کنیم که برای این منظور کلاس ActionClass را باز کرده و کدی به صورت زیر داخل آن می‌نویسیم:

public class ActionClass {
    public static void main(String[] args) {
        Grandfather grandfatherObject = new Grandfather();
        grandfatherObject.showGrandfatherHeight();
        grandfatherObject.showGrandfatherSkinColor();
        grandfatherObject.showGrandfatherBaldness();
        grandfatherObject.showGrandfatherBehavior();
        grandfatherObject.showGrandfatherCreativity();
        grandfatherObject.showGrandfatherActivity();
        grandfatherObject.showGrandfatherNationality();
    }
}

همان‌طور که در کد فوق ملاحظه می‌کنیم، پس از ساخت آبجکتی از روی کلاس Grandfather تحت عنوان grandfatherObject می‌توانیم متدهای تعریف‌شده در این کلاس را روی آبجکت مذکور فراخوانی نماییم. حال پس از اجرای برنامۀ فوق در خروجی خواهیم داشت:

Short
Bright
Bald
Angry
Very Creative
Active
Iranian

همان‌طور که می‌بینیم، کلیۀ اتریبیوت‌های تعریف‌شده برای کلاس Grandfather در کنسول نمایش داده می‌شوند. اکنون به تعریف ویژگی‌های کلاس Father می‌پردازیم و برای این منظور فایل کلاس مذکور را باز کرده و در ابتدا کد آن را به صورت زیر تغییر می‌دهیم:

public class Father extends Grandfather {

}

در کد فوق، کلیدواژۀ extends بدین منظور به کار گرفته شده است تا کلاس Father خصوصیات خود را از کلاس Grandfather ارث‌بری کند. به عبارت دیگر، از این پس کلاس Father کلیهٔ ویژگی‌های کلاس Grandfather را دارا است اما همان‌طور که در آموزش قبل بیان کردیم، برخی خصوصیات پدر با پدربزرگ متفاوت می‌باشد که از آن جمله می‌توان به خوش‌اخلاق بودن پدر و همچنین خلاقیت کمتر وی اشاره کرد و از همین روی نیاز داریم تا برخی از اتریبیوت‌هایی که کلاس Father از کلاس Grandfather به ارث برده است را به اصطلاح Override نماییم (همچنین در آموزش قبل بدین موضوع اشاره کردیم که پدر ویژگی‌های دیگری همچون تحصیلات لیسانس و میزان مطالعۀ زیاد دارا است که به علاوۀ سایر خصوصیاتی می‌باشند که از پدربزرگ به ارث برده است.)

به منظور حل مسائلی از این دست در زبان برنامه‌نویسی جاوا، از مفهوم Override استفاده می‌کنیم. به عبارت دیگر، زمانی که بخواهیم که یک Subclass برخی از ویژگی‌های Superclass را به ارث نبرد، می‌باید خصوصیات مد نظر را Override یا «رونویسی» نماییم که برای این منظور، کلاس Father را به شکل زیر اُورراید می‌کنیم:

public class Father extends Grandfather {
    @Override
    public void showGrandfatherBehavior() {
        String behavior = "Well-behaved";
        System.out.println(behavior);
    }
    @Override
    public void showGrandfatherCreativity() {
        String creativity = "Creative";
        System.out.println(creativity);
    }
}

در حقیقت، اُورراید کردن بدین شکل صورت می‌گیرد که پیش از نوشتن نام متد مربوطه، دستور Override@ را نوشته سپس تغییرات مد نظر را در اتریبیوت‌های آن متد اِعمال می‌نماییم. همان‌طور که در کد فوق مشاهده می‌کنیم، از میان تمامی متدهای موجود در کلاس Grandfather تنها دو متدی را نوشته‌ایم که نیاز به اُورراید شدن داشتند و در ادامه اتریبیوت‌های آن‌ها را تغییر داده‌ایم. در واقع، مقدار منتسب به اتریبیوت behavior را معادل با استرینگ «Well-behaved» در نظر گرفته و همچنین مقدار اتریبیوت creativity را معادلِ استرینگ «Creative» قرار داده‌ايم.

اکنون خصوصياتی را به کلاس Father اضافه می‌کنیم که پدربزرگ آن‌ها را نداشته و خصوصیاتی منحصربه‌فرد برای پدر می‌باشند که این خصوصیات عبارتند از تحصیلات لیسانس و میزان مطالعهٔ زیاد پدر که به منظور اضافه کردن آن‌ها به روش زیر عمل می‌کنیم:

public class Father extends Grandfather {
    @Override
    public void showGrandfatherBehavior() {
        String behavior = "Well-behaved";
        System.out.println(behavior);
    }
    @Override
    public void showGrandfatherCreativity() {
        String creativity = "Creative";
        System.out.println(creativity);
    }
    public void showFatherEducation() {
        String education = "BA";
        System.out.println(education);
    }
    public void showFatherStudyTime() {
        String studyTime = "Much";
        System.out.println(studyTime);
    }
}

همان‌طور که ملاحظه می‌کنیم، متد جدیدی تحت عنوان ()showFatherEducation تعریف کرده و داخل آن گفته‌ایم در صورت فراخوانی آن، آبجکتی به نام education از روی کلاس String ساخته شده و استرینگ «BA» بدان منتسب گردد و در ادامه متغیر education به عنوان آرگومان ورودی به متد ()println پاس داده شده و بدین ترتیب استرینگ اختصاص‌یافته به education در کنسول نمایش داده می‌شود.

سپس متدی دیگری تحت عنوان ()showFatherStudyTime ایجاد کرده و آبجکتی به نام studyTime از روی کلاس String ساخته و استرینگ «Much» را بدان منتسب می‌کنیم تا در صورت فراخوانی متد مذکور، استرینگ مربوطه به عنوان ورودی به متد ()println پاس داده شده و در کنسول چاپ گردد (دو متد ()showFatherStudyTime و ()showFatherEducation صرفاً مخصوص کلاس Father بوده و سایر کلاس‌ها به متدهای مذکور دسترسی ندارند.)

حال برای تست عملکرد کلاس Father آبجکتی از روی کلاس مذکور در کلاس ActionClass ساخته و متدهای تعریف‌شده در کلاس Father را روی آبجکت مربوطه فراخوانی می‌نماییم که برای این منظور می‌توانیم به روش زیر عمل کنیم:

public class ActionClass {
    public static void main(String[] args) {
        // Grandfather grandfatherObject = new Grandfather();
        // grandfatherObject.showGrandfatherHeight();
        // grandfatherObject.showGrandfatherSkinColor();
        // grandfatherObject.showGrandfatherBaldness();
        // grandfatherObject.showGrandfatherBehavior();
        // grandfatherObject.showGrandfatherCreativity();
        // grandfatherObject.showGrandfatherActivity();
        // grandfatherObject.showGrandfatherNationality();
        Father fatherObject = new Father();
        fatherObject.showGrandfatherHeight();
        fatherObject.showGrandfatherSkinColor();
        fatherObject.showGrandfatherBaldness();
        fatherObject.showGrandfatherBehavior();
        fatherObject.showGrandfatherCreativity();
        fatherObject.showGrandfatherActivity();
        fatherObject.showGrandfatherNationality();
        fatherObject.showFatherEducation();
        fatherObject.showFatherStudyTime();
    }
}

کدهای مربوط به کلاس Grandfather را به منظور جلوگیری از شلوغ شدن خروجی برنامه کامنت کرده‌ایم و در ادامه آبجکتی از روی کلاس Father به نام fatherObject ساخته و کلیۀ متدهای مربوط به کلاس Grandfather به همراه دو متد جدید مربوط به کلاس Father را روی آبجکت fatherObject فراخوانی کرده‌ایم و با اجرای برنامۀ فوق در خروجی خواهیم داشت:

Short
Bright
Bald
Well-behaved
Creative
Active
Iranian
BA
Much

همان‌طور که در خروجی برنامه مشاهده می‌کنیم، کلاس Father کلیۀ خصوصیات کلاس Grandfather را به ارث برده است با این تفاوت که عملکرد متدهای ()showGrandfatherBehavior و ()showGrandfatherCreativity را در کلاس Father تغییر داده‌ایم بدین صورت که مقدار اتریبیوت‌های behavior و creativity را در آن به ترتیب معادل استرینگ‌های «Well-behaved» و «Creative» قرار داده و به عبارتی متدهای مذکور را اُورراید کرده‌ایم. به علاوه، متدهای دیگری هم به کلاس Father افزوده‌ایم تا در آن‌ها اتریبیوت‌هایی همچون «BA» و «Much» را تعریف نماییم.

در این مرحله به پیاده‌سازی کلاس Son می‌پردازیم به طوری که کلاس مذکور از کلاس Father ارث‌بری نماید که برای این منظور فایل کلاس Son را باز نموده و آن را به صورت زیر تکمیل می‌کنیم:

public class Son extends Father{

}

در حقیقت، از آنجایی که می‌خواهیم کلاس Son کلیۀ خصوصیات خود را از کلاس Father به ارث ببرد، کلیدواژۀ extends را نوشته سپس نام کلاس Father را می‌نویسیم. نکتۀ قابل‌توجه در ارتباط با ارث‌بری کلاس Son از کلاس Father این است که به دلیل ارث‌بری کلاس Father از کلاس Grandfather و همچنین ارث‌بری کلاس Son از کلاس Father می‌توان گفت که کلاس Son آن دسته از خصوصیاتی که کلاس Father از کلاس Grandfather به ارث برده را نیز به ارث می‌برد که برای درک بهتر آنچه که در بالا بدان اشاره کردیم، به آموزش قبل مراجعه کرده و می‌بینیم که پسر خصوصیاتی به صورت زیر دارا است:

- بر خلاف پدر و پدربزرگ خود، قدبلند است.
- اصلاً طاس نیست.
- همانند پدربزرگ خود عصبانی است.
- بر خلاف پدرش، تحصیلات فوق‌لیسانس دارد.
ـ در نهایت خیلی هم اهل مطالعه است.

حال با آگاهی از این موارد، می‌توانیم کد مربوط به کلاس Son را به صورت زیر ویرایش نماییم:

public class Son extends Father {
    @Override
    public void showGrandfatherHeight() {
        String height = "Tall";
        System.out.println(height);
    }
    @Override
    public void showGrandfatherBaldness() {
        String baldness = "Not Bald";
        System.out.println(baldness);
    }
    @Override
    public void showGrandfatherBehavior() {
	String behavior = "Angry";
	System.out.println(behavior);
    }
    @Override
    public void showFatherEducation() {
        String education = "MA";
        System.out.println(education);
    }
    @Override
    public void showFatherStudyTime() {
        String studyTime = "Very Much";
        System.out.println(studyTime);
    }
}

همان‌طور که در کد فوق مشاهده می‌کنیم، با استفاده از دستور Override@ متد ()showGrandfatherHeight را اُورراید می‌کنیم بدین صورت که مقدار اتریبیوت height را برابر با استرینگ «Tall» قرار می‌دهیم مضاف بر اینکه متد ()showGrandfatherBaldness را نیز اُورراید کرده و مقدار اتریبیوت baldness در آن را برابر با استرینگ «Not Bald» قرار می‌دهيم. به همين ترتيب، متدهای ()showGrandfatherBehavior و ()showFatherEducation و همچنین متد ()howFatherStudyTime اُورراید شده و به ترتیب اتریبیوت‌های behavior ،education و studyTime در آن‌ها برابر با استرینگ‌های «Angry» ،«MA» و «Very Much» قرار داده می‌شوند.

سؤالی که در اینجا ممکن است مطرح شود این است که «چرا متد مربوط به اتریبیوت behavior در کلاس Son از کلاس Grandfather اُورراید شده است در حالی که این خصوصیت مابین پسر و پدربزرگ مشترک می‌باشد؟» در پاسخ بدین سؤال می‌توان گفت که پدر کلیهٔ خصوصیات خود را از پدربزرگ به ارث برده اما بر خلاف پدربزرگ فردی خوش‌اخلاق است و از همین روی متد ()showGrandfatherBehavior در کلاس Father از کلاس Grandfather اُورراید شده و مقدار اتریبیوت behavior از کلاس Grandfather به استرینگ «Well-behaved» در این کلاس تغییر یافته است. حال از آنجایی که پسر بر خلاف پدر فردی عصبانی بوده و همچنین خصوصیت عصبانی بودن را از پدربزرگ خود به ارث برده است، مجدداً مجبور به اُورراید کردن متد مربوط به اتریبیوت behavior در کلاس Son از کلاس Grandfather می‌باشیم. اکنون می‌توانیم به کلاس ActionClass رفته و آن را به شکل زیر بازنویسی کنیم:

public class ActionClass {
    public static void main(String[] args) {
        // Grandfather grandfatherObject = new Grandfather();
        // grandfatherObject.showGrandfatherHeight();
        // grandfatherObject.showGrandfatherSkinColor();
        // grandfatherObject.showGrandfatherBaldness();
        // grandfatherObject.showGrandfatherBehavior();
        // grandfatherObject.showGrandfatherCreativity();
        // grandfatherObject.showGrandfatherActivity();
        // grandfatherObject.showGrandfatherNationality();
        // Father fatherObject = new Father();
        // fatherObject.showGrandfatherHeight();
        // fatherObject.showGrandfatherSkinColor();
        // fatherObject.showGrandfatherBaldness();
        // fatherObject.showGrandfatherBehavior();
        // fatherObject.showGrandfatherCreativity();
        // fatherObject.showGrandfatherActivity();
        // fatherObject.showGrandfatherNationality();
        // fatherObject.showFatherEducation();
        // fatherObject.showFatherStudyTime();
        Son sonObject = new Son();
        sonObject.showGrandfatherHeight();
        sonObject.showGrandfatherSkinColor();
        sonObject.showGrandfatherBaldness();
        sonObject.showGrandfatherBehavior();
        sonObject.showGrandfatherCreativity();
        sonObject.showGrandfatherActivity();
        sonObject.showGrandfatherNationality();
        sonObject.showFatherEducation();
        sonObject.showFatherStudyTime();
    }
}

همان‌طور که در کد فوق مشاهده می‌شود، کلیۀ کدهای پیشین را کامنت کرده‌ایم سپس آبجکتی از روی کلاس Son به اسم sonObject ساخته‌ و در ادامه کلیۀ متدهای تعریف‌شده در کلاس مذکور را روی آبجکت ساخته‌شده از روی آن فراخوانی کرده‌ایم که پس از اجرای برنامۀ فوق در خروجی خواهیم داشت:

Tall
Bright
Not Bald
Angry
Creative
Active
Iranian
MA
Very Much

همان‌طور که مشاهده می‌کنیم، خروجی اول نشانگر اولین اُورراید در کلاس Son است که در آن متد ()showGrandfatherHeight را رونویسی کردیم و خروجی دوم مربوط به اُورراید کردن متد ()showGrandfatherBaldness می‌باشد و به همین ترتیب خروجی حاصل از اُورراید کردن متدهای دیگر را مشاهده‌ می‌کنیم.

دانلود فایل‌های تمرین

لیست نظرات
کاربر میهمان
دیدگاه شما چیست؟
کاربر میهمان
reeza3533
reeza3533
۱۳۹۸/۰۴/۲۱
سلام دمتون گرم.
یه سؤال دارم اگه بخواهیم پسر یکی از ویژگی‌های پدر رو به ارث نبره باید چکار کنیم؟اصن این‌طور میشه؟
ممنون میشم توضیح بدین.
winner.expert2
winner.expert2
۱۳۹۸/۰۲/۲۵
بسیار عالی و قابل‌فهم
پیشنهاد میکنم که متدهای مثل showGrandfatherHeight رو به showHeight تغییر نام بدید.
کاربر میهمان
کاربر میهمانمن یک کاربر مهمان هستم
۱۳۹۷/۰۹/۱۲
بسیار عالی دست استاد درد نکنه خدا کلاس اجداد تو بیامرزد
کاربر میهمان
نیمامن یک کاربر مهمان هستم
۱۳۹۵/۰۶/۰۱
سلام ، اگر عبارت Override رو باطل کردن ترجمه کنیم بهتر نیست ؟ در واقع ما با Override کردن داریم خصوصیات قبلی رو ابطال می کنیم و خصوصیات جدید رو جایگزین می کنیم . معنی Override توی دیکشنری هم همین باطل کردن یا لغو کردن هست .
کاربر میهمان
Hiradمن یک کاربر مهمان هستم
۱۳۹۵/۰۳/۱۰
بدون شک بهترین درسی بود که تاحالا توی اینترنت دیدم..
دست مریزاد واقعا
mohammad m
mohammad m
۱۳۹۵/۰۱/۱۴
ممنون از اموزشهاتون
کاربر میهمان
محمدحسین زرچیمن یک کاربر مهمان هستم
۱۳۹۴/۱۲/۱۱
با سلام و عرض خسته نباشید ...
انصافا خیلی قشنگ بود.
و اگر امکانش هست طریقه اتصال جاوا به دیتا بیس رو هم توضیح کامل بدین؟!
ممنون.
حامد هردانی
حامد هردانی
۱۳۹۴/۱۱/۰۲
سلام
مرسی از آموزشهاتون
فقط یه مورد. طاس میشه Bald.
Bold معنی طاس نمیده
کاربر میهمان
ارزیمن یک کاربر مهمان هستم
۱۳۹۴/۱۰/۱۵
ممنون از آموزشاتون بیشتر درمورد شی گرایی آموزش بگذارید
محمد عقبائی فرهنگ
محمد عقبائی فرهنگ
۱۳۹۴/۰۸/۱۸
ببخشید من وقتی @Override رو نمینویسم هم کار میکنه. علتش چی میتونه باشه؟ یعنی خودش متوجه میشه که Override هستش؟