آموزش جاوا (فصل ۸)
مقدمهای بر مفهوم شیئگرایی در زبان برنامهنویسی جاوا
در این مرحله از آموزش قصد داریم تا نگاه دقیقتری به مفاهیم شیئگرایی داشته باشیم که در زبانهای برنامهنویسی متعددی همچون جاوا مورد استفاده قرار میگیرند. در حقیقت، شیئگرایی عبارت است از پارادایمها و استراتژیهایی که در زبانهای برنامهنویسی بسیاری به کار گرفته میشوند.
پایه و اساس رویکرد Object Oriented Programming یا به اختصار OOP به معنی «برنامهنویسی شیئگرا» این است که نگاه برنامهنویسان به توسعۀ نرمافزار به نگاه ایشان به دنیای واقعی نزدیکتر گردد. به طور مثال، سیستمی را در نظر بگیرید که با استفاده از آن به مطالعۀ این آموزش مشغول هستید که میتوان گفت این سیستم یک Object یا شیئ در دنیای واقعی میباشد و این در حالی است که شیئ متعلق به شما با کامپیوتر (شیئ) مورد استفاده توسط مؤلف این دوره بسیار متفاوت میباشد.
برای درک بهتر مفهوم شیئگرایی مثالی را مد نظر قرار میدهیم که در آن قرار است تا یک آپارتمان ده طبقه بسازیم که برای هر یک از طبقات آن یکسری ویژگی خاص و منحصربهفرد تعریف کردهایم بدین صورت که تصمیم داریم تا رنگ دیوارهای طبقۀ اول را سفید و درهای آن را کِرِم رنگ کنیم و رنگ دیوارهای طبقۀ هفتم را صورتی و برای درهای آن نیز رنگ سفید را انتخاب کردهایم و به همین ترتیب برای سایر طبقات نیز برخی ویژگیهای منحصربهفرد دیگر در نظر گرفتهایم. در حقیقت، در این مثال هر یک از طبقههای آپارتمان مانند یک آبجکت در زبان برنامهنویسی شیئگرایی همچون جاوا است بدین معنی که با وجود در نظر گرفتن یکسری ویژگی خاص برای هر یک از طبقات، باز هم کلیۀ طبقات آپارتمان از یکسری ویژگی مشترک همچون اتاق خواب، سرویس، آشپزخانه و پنجره برخوردارند.
در زبان برنامهنویسی جاوا نیز یک کلاس دقیقاً چنین نقشی را ایفا میکند؛ به عبارت دیگر، در یک برنامۀ نوشتهشده به زبان جاوا کلاسها به منزلۀ یک نقشۀ خانه هستند که هر یک از طبقات آپارتمان از روی آنها ساخته میشوند و هر یک، علیرغم داشتن یک پِلن ثابت، دارای یکسری ویژگی منحصربهفرد هستند که اصطلاحاً به هر یک از آنها یک آبجکت (شییٔ) گفته میشود.
در زبانهای برنامهنویسی شیئگرا دولوپرها یک کلاس با خصوصیات مد نظر خود را تعریف کرده سپس از روی کلاس تعریفشده یکسری آبجکت میسازند به طوری که هر یک از آبجکتها دارای زیربنایی یکسان اما نمایی متفاوت میباشند! به طور کلی، ویژگی شیئگرایی یکسری مزایای منحصربهفرد را به زبان برنامهنویسی جاوا افزوده است که در ادامه قصد داریم تا در قالب مثال آپارتمان به بررسی برخی از ویژگیهای این سَبک برنامهنویسی در زبان جاوا بپردازیم.
در مثال فوق، ابتدا یک نقشۀ کلی (کلاس) برای آپارتمان طراحی میکنیم و در ادامه تصمیم میگیریم که تعدادی از طبقات آپارتمان خود را سهخوابه و برخی از آنها را چهارخوابه بسازیم که در اینجا میتوانیم از روی پِلن (کلاس) اصلی دو کلاس دیگر تحت عناوین سهخوابه و چهارخوابه طراحی کنیم که ویژگیهای کلاس اصلی را اصطلاحاً به ارث ببرند. در زبان برنامهنویسی جاوا این مفهوم Inheritance به معنی «ارثبری» نام دارد که در آن به کلاس اولیه اصطلاحاً Superclass یا «کلاس اصلی» گفته میشود و بدین ترتیب کلاسهای مربوط به طبقات آپارتمان سهخوابه و چهارخوابه نیز Subclass یا «کلاس زیرشاخه» میباشند. به عبارت دیگر، میتوان گفت که کلاس اصلی تحت عنوان کلاس Parent (والد) بوده و کلاسهای زيرشاخۀ آن کلاس Child (فرزند) نامیده میشوند و نکتهای که در اینجا میباید حتماً مد نظر قرار دهیم این است که در برنامهنویسی هر کلاس به تنهایی منجر به ایجاد چیزی نخواهد شد چرا که یک مفهوم کاملاً انتزاعی است و چنانچه بخواهیم نمود عینی یک کلاس را ببینیم، نیاز به ساخت یک آبجکت از روی آن داریم.
در حقیقت، پیش از رواج زبانهای برنامهنویسی شیئگرا، برنامهنویسان در صورت نیاز به اِعمال تغییر در بخشی از برنامۀ خود مجبور به تغییر بخشهای قابلتوجهی از برنامۀ مد نظر بودند اما پس از توسعۀ زبانهای برنامهنویسی شیئگرایی همچون جاوا این مشکل تا حد بسیاری حل شد چرا که با اِعمال تغییر روی یک کلاس، کلیۀ ویژگیهای مربوط به آبجکتهای ساختهشده از روی آن کلاس نیز تغییر میکنند به طوری که ویژگیهای جدید و تغییرات اِعمالشده را از کلاس جدید به ارث میبرند.
برای درک بهتر مفهوم شیئگرایی به ذکر مثال دیگری میپردازیم که در آن از سیستمهای کامپیوتری به عنوان نمونه استفاده میکنیم. در واقع، هر یک از سیستمهای کامپیوتری شیئی هستند که میتوانند چندین شیئ دیگر همچون هارد، سیپییو، رم و غیره را در خود جای دهند و هر یک دارای یکسری به اصطلاح Attribute به معنی «خصیصه» میباشند که آنها را از دیگر کامپیوترها مجزا میسازند (برای مثال، یک سیستم دارای سیپییوی هشتهستهای میباشد.) در عین حال، هر کامپیوتر دارای یکسری به اصطلاح Behavior یا «عملکرد» نیز میباشد. به طور مثال، هر سیستمی میتواند یک فایل صوتی را پخش کند که رفتاری مختص یک شیئ از نوع کامپیوتر میباشد در حالی که اشیاء دیگری در دنیای واقعی مانند یخچال دارای چنین رفتاری نمیباشند (معادل فارسی واژۀ Behavior «رفتار» است اما به منظور انتقال بهتر مفهوم، معنای «عملکرد» را در این آموزش در نظر گرفتهایم.)
به طور کلی، در برنامهنویسی شیئگرا، آبجکتها داری سه ویژگی کلیدی میباشند که در ادامه به توصیف هر یک میپردازیم:
– ویژگی اول اینکه هر شیئ دارای یک به اصطلاح Identity یا «شناسۀ» خاص خود است. به طور مثال، دو انسان که به منزلۀ شیئ هستند را در نظر بگیرید که هر دو دارای یکسری خصوصیت مشترک همچون داشتن دست، پا، قدرت تفکر و غیره میباشند و همچنین عملکرد مشابهی مانند حرف زدن، دویدن و سایر مواردی از این دست دارند اما در عین حال دارای دو شناسۀ مجزا میباشند به طوری که ممکن است مثلاً نام یکی احسان و دیگری نیما باشد.
– ویژگی دوم مربوط به Attribute یا «خصیصه» میباشد که برای مثال میتوان به قد احسان معادل عددی همچون 185 سانتیمتر اشاره کرد در حالی که قد نیما 176 سانتیمتر میباشد.
– در نهایت هم ویژگی سوم تحت عنوان Behavior یا «عملکرد» آن شیئ میباشد به طوری که مثلاً احسان میتواند به خوبی پیانو بنوازد اما نیما یک برنامهنویس خوب است.
همچنین توجه داشته باشیم که اشیاء ساختهشده در برنامه میباید حتماً ریشه در جایی داشته باشند؛ به عبارت بهتر، آبجکتها به خودی خود ایجاد نمیشوند بلکه کلاسها هستند که به آبجکتها معنا و مفهوم میدهند. در حقیقت، در برنامهنویسی شیئگرا به منظور ساخت آبجکت ابتدا نیاز داریم تا کلاس متناظر آن را تعریف کرده سپس آبجکت مورد نیاز خود را از روی آن بسازیم. به عبارت دیگر، کلاسی با مجموعۀ خصیصهها و رفتارهای مد نظر خود تعریف کرده و بدین ترتیب مشخص میکنیم که آبجکتهای ساختهشده از روی آن دارای چه خصیصهها و رفتارهایی باشند و از همین روی میتوان گفت که هر آبجکت به منزلۀ یک Instance یا «نمونه» از کلاس متناظرش میباشد.
در برنامهنویسی به ساخت آبجکت از روی کلاس اصطلاحاً Instantiation یا «نمونهسازی» گفته میشود و همانطور که پیشتر اشاره کردیم، برای ساخت آبجکت از روی یک کلاس در ابتدا نیاز به تعریف کلاس مربوطه داریم. به علاوه، نکتهای که در اینجا میباید مد نظر قرار دهیم این است که برنامهنویسان الزماً مجبور نیستند تا کلیۀ کلاسها را خودشان توسعه دهند چرا که طراحان زبانهای برنامهنویسی بسیاری از آنها را در قالب یکسری کلاس از پیش تعریفشده در زبان برنامهنویسی مربوطه پیادهسازی کردهاند که به راحتی میتوان با بهکارگیری کلاس یا کلاسهای مد نظر از قابلیتهای آنها بهرهمند شد اما در عین حال چنانچه بخواهیم کلاسی متناسب با ویژگیهای مورد نیاز خود را بنویسیم، همواره میباید چهار مفهوم اصلی را در تعریف آن مد نظر قرار دهیم که عبارتند از Polymorphism ،Inheritance ،Encapsulation و Abstraction که میتوان آنها را به عنوان ویژگیهای کلیدی شیئگرایی قلمداد کرد (به منظور به خاطر سپردن چهار عنصر اصلی ساخت کلاس که در بالا بدان اشاره کردیم، میتوان حرف اول هر یک از چهار کلمه را گرفته و کلمۀ A PIE به معنی «یک کلوچه» را به خاطر سپرد.)
آشنایی با مفهوم Abstraction
یکی از ویژگیهای برنامهنویسی شیئگرا، بهکارگیری مفهومی تحت عنوان Abstraction است که در اینجا به توصیف مفهوم این واژه در قالب یک مثال از دنیای واقعی میپردازیم. به طور مثال، شرایطی را در نظر بگیرید که از دوست خود خواهش میکنید تا لیوان آبی را از روی میز به شما بدهد که در این صورت صرفاً نمود خارجی میز را مد نظر قرار داده و هرگز از دوست خود نمیخواهید تا مثلاً لیوان آبی از روی میز قهوهای، با عرض دو متر، طول سه متر و ارتفاع یکونیم متر را به شما بدهد بلکه صرفاً مفهوم کلی میز را مد نظر قرار دادهاید.
Abstraction در برنامهنویسی نیز به همین صورت است که در حین نوشتن کلاسها دقیقاً میباید یک مفهوم کلی را در نظر بگیریم. به طور مثال، فرض کنید که برنامهای برای یک باشگاه بدنسازی مینویسیم که برای این منظور نیاز به تعریف یک کلاس مرتبط با ثبتنام ورزشکاران داریم و در برنامۀ خود قصد داریم تا کارهایی همچون ثبتنام، ویرایش اطلاعات ورزشکاران، ثبت فیش واریزی و غیره را از طریق کلاس مذکور انجام دهیم که در پیادهسازی چنین کلاسی میباید صرفاً یکسری مفهوم کلی را در نظر گرفته و در صورت لزوم آبجکتهایی به صورت اصطلاحاً Customized از روی آن ایجاد کنیم تا نیازمان را برآورده سازند.
آشنایی با مفهوم Encapsulation
تاکنون بسیار برایمان پیش آمده است که سرما خورده و به پزشک مراجعه کرده باشیم تا دارویی جهت بهبود حال خود تهیه کنیم و برخی از این داروها به شکل کپسول بودهاند که در این مثال وظیفۀ کپسول نگهداری دارو و محافظت از آن است. در برنامهنویسی شیئگرا نیز Encapsulation مفهومی مشابه دارا است به طوری که یکسری Attribute و Behavior تعریفشده برای آبجکت ساختهشده از روی کلاس را در کنار یکدیگر نگهداری میکند اما در عین حال Encapsulation وظیفهای فراتر از این موضوع داشته و امکانی را برای دولوپرها فراهم میکند تا بدین طریق از میان مجموعۀ اتریبیوتهای یک آبجکت، تنها برخی اتریبیوتهای مورد نیاز را در توسعۀ نرمافزار مربوطه انتخاب کرده و در دسترس سایر بخشهای برنامه قرار دهند؛ به عبارت دیگر، سایر بخشهای برنامه از میان مجموعۀ اتریبیوتهای یک آبجکت صرفاً به یکسری اتریبیوت خاص دسترسی خواهند داشت.
سؤالی که در اینجا ممکن است برای برخی برنامهنویسان مبتدی پیش آید این است که «چه لزومی دارد که ما چیزی را در کلاس خود ایجاد کنیم سپس خود را از دسترسی به آن منع کنیم؟» که در پاسخ به این پرسش میتوان گفت با این کاروابستگی مابین بخشهای مختلف برنامه را به حداقل میرسانیم به نحوی که ایجاد یک تغییر کوچک در بخشی از برنامه منجر به تخریب دیگر بخشها نگردد. نکتهEncapsulation یا پنهانسازی بخشی از کلاس از سایر بخشهای برنامه را هر چه بیشتر در برنامۀ خود رعایت کنیم مدیریت برنامه در درازمدت راحتتر خواهد شد؛ بدین معنی که در برنامههای نسبتاً بزرگ هر چه وابستگی مابین بخشهای مختلف برنامه کمتر باشد، دولوپرها در توسعه و نگهداری سورسکد مربوطه با سردرگمی کمتری مواجه خواهند شد.
آشنایی با مفهوم Inheritance
Inheritance در برنامهنویسی شیئگرا امکانی را در اختیار برنامهنویسان قرار میدهد تا بدین وسیله به جای نوشتن یک کلاس از ابتدا، برخی از ویژگیهای کلاس جدید را از کلاس دیگری اصطلاحاً به ارث ببرند. برای درک بهتر این موضوع، به مثال مربوط به آپارتمان باز میگردیم که در آن یک نقشۀ کلی برای آپارتمان ده طبقه داشتیم که به منزلۀ یک کلاس برای کلیۀ طبقات محسوب میشد. در چنین شرایطی اگر بخواهیم برخی طبقات را مثلاً سهخوابه یا چهارخوابه بسازیم، به هیچ وجه نیازی به نوشتن یک کلاس از پایه نداریم بلکه به راحتی میتوانیم یک کلاس با خصوصیت سه یا چهار خوابه بنویسیم که دیگر خصوصیات خود را از کلاس اصلی یا همان Superclass به ارث ببرد که در این صورت چنانچه تغییری در Superclass اِعمال کنیم، تغییر ایجادشده در هر یک از کلاسهای زیرشاخۀ سهخوابه و چهارخوابه نیز اِعمال خواهد شد (در زبان برنامهنویسی جاوا هر یک از کلاسها در آن واحد قابلیت ارثبری از صرفاً یک کلاس اصلی را دارند.)
آشنایی با مفهوم Polymorphism
Polymorphism به معنی «چندفرمی» یا «چندریختی» است که برای روش شدن مفهوم آن، به ذکر مثالی از دنیای واقعی میپردازیم. به طور مثال، حیوان سگ را مد نظر قرار میدهیم که چنانچه این حیوان دادهای از جنس بوی آدم غریبه به حس بویاییاش منتقل شود «واق واق» میکند و در صورت اِستشمام بوی دادهای از جنس گوشت، بزاقش ترشح میشود به علاوه اینکه در صورت احساس بوی صاحبش دُمش تکان میدهد. در این مثال، در هر سه حالت حس بویایی سگ است که فعالیت میکند و تنها تفاوت در نوع دادهای است که به حس بویایی این حیوان منتقل شده است.
در زبان برنامهنویسی جاوا نیز برای مثال میتوان به علامت +
اشاره کرد که دقیقاً ویژگی مشابه به مثال فوق را دارا است به طوری که چنانچه دو متغیر از جنس عدد را با عملگر +
جمع کنیم، حاصلجمع آن دو عدد در خروجی ریترن میشود که برای مثال حاصل جمع دو عدد 5 و 7 به صورت 12 = 7 + 5 بوده و عدد 12 در خروجی ریترن میشود اما این در حالی است که اگر از این علامت در کنار دو متغیر از جنس استرینگ استفاده کنیم، استرینگهای منتسب به متغیرهای مذکور در کنار یکدیگر قرار گرفته و در معرض دیدمان قرار میگیرند. برای مثال، میتوان دو استرینگ «Hello» و «World» را نام برد که اِعمال عملگر +
روی آنها منجر بدین میشود تا دو استرینگ به صورت «Hello + World» در کنار یکدیگر قرار گرفته و اصطلاحاً با یکدیگر Concate (الحاق) شوند و در نهایت استرینگ «HelloWorld» در خروجی چاپ شود.
در دنیای واقعی، یک شیئ میتواند جدای از دیگر اشیاء یا در تعامل با آنها باشد و حتی چندین شیئ میتوانند با یکدیگر ادغام شده و شیئ جدیدی را به وجود آورند.
به طور کلی، از جمله مزایای تعریف کلاس و ساخت آبجکت از روی آن به منظور استفاده در جایجای برنامه میتوان به سهولت استفاده از متدها و اتریبیوتهای کلاس مد نظر اشاره کرد که با ساخت آبجکت به تعداد دلخواه از روی کلاس مد نظر میتوان به متد یا اتریبیوت مورد نظر خود از کلاس مربوطه دسترسی پیدا کرد به علاوه اینکه اِعمال تغییر در هر یک از اتریبیوتهای کلاس مد نظر به راحتی روی آبجکت ساختهشده از روی آن اِعمال میشود.
از سوی دیگر برنامهنویسی با استفاده از سبک شیئگرایی منجر بدین میشود که فرآیند دیباگ کردن سورسکد به سادگی انجام پذیرد چرا که به جای درگیر کردن خود با کل پروژه میتوانیم به رفع ارورهای احتمالی در کلاس مورد نظر بپردازیم (برای آشنایی بیشتر با فرایند دیباگ کردن، میتوانید به آموزش فرایند دیباگ کردن در برنامهنویسی به چه معنا است؟ از دوره ی آموزش اصول برنامهنویسی در سکان آکادمی مراجعه نمایید.)
اکنون به منظور درک بهتر هر یک از مفاهیم کلاس و آبجکت نیاز است تا پروژهای تحت عنوان OOP ایجاد مکنیم سپس کلاسی به نام MyClass
در آن میسازیم. در این مرحله کد ما میباید به شکل زیر باشد:
public class MyClass {
}
حال اگر آبجکتی از روی کلاس مذکور ایجاد کنیم، آبجکت ساختهشده قابلیت استفاده به منظور انجام هیچ تَسکی را نخواهد داشت چرا که کلاس مربوطه دارای هیچ گونه اتریبیوت و متدی نمیباشد.
همانطور که میبینیم، به منظور تعریف کلاس از کیورد public
استفاده کردهایم که معنای لغوی آن معادل «عمومی» بوده و در زبان برنامهنویسی جاوا به منظور تعریف سطح دسترسی پابلیک مورد استفاده قرار میگیرد بدین معنی که کلاس مذکور از دیگر بخشهای برنامه نیز قابلدسترسی خواهد بود (در آموزش آشنایی با انواع سطوح دسترسی در زبان برنامهنویسی جاوا با دیگر سطوح دسترسی همچون private
و protected
نیز آشنا خواهیم شد.)
حال نیاز داریم تا یک اتریبیوت برای کلاس فوق تعریف کنیم که برای این منظور میتوانیم با استفاده از دیتا تایپ مورد نظر خود متغیرهایی تعریف کرده و در ادامه با ساخت آبجکت از روی کلاس به اتریبیتوت مد نظر دسترسی بیایم. بنابراین با استفاده از دیتا تایپ int
متغیری به نام number
در کلاس فوق تعریف کرده و عدد 1 را بدان اختصاص میدهیم و در ادامه یک متد برای کلاس تعریف میکنیم و در ادامه میتوانیم متد مورد نظر را روی آبجکت ساختهشده از روی آن فراخوانی نماییم. به طور خلاصه، وظیفۀ متدها انجام یکسری تَسکهای محولشده به آنها در برنامه است که در کد زیر متدی به منظور نمایش استرینگی فرضی در کنسول تعریف میکنیم:
public void showSomething() {
}
در واقع، با قرار دادن کلیدواژۀ public
قبل از نام متد، این دستور را به کامپایلر میدهیم که متد ()showSomething
از تمامی نقاط برنامه در دسترس خواهد بود مضاف بر اینکه کلیدواژۀ void
نیز بدین معنی است که متد مذکور از یکسو قرار است تا یکسری دستور را اجرا نماید و از سوی دیگر هیچ گونه دیتایی را در خروجی ریترن نمیکند. نکتهدر نامگذاری متدها همواره از واژگانی به صورت فعل استفاده میکنیم چرا که وظیفۀ اصلی متدها انجام یکسری کار است و از همین روی بهتر است تا نامگذاری آنها هم به نحوی باشد که گویای وظیفهشان باشد.
با توجه به آنچه که در نکتۀ فوق بیان کردیم، متد ()showSomething
نیز وظیفۀ نمایش یک استرینگ در کنسول را دارا است و از همین روی نام متد مذکور را با واژۀ «show» آغاز کردهایم. پس از نام متد نیز علامتهای ( )
را قرار داده سپس علائم { }
را قرار میدهیم و در ادامه دستورات مد نظر به منظور اجرا در صورت فراخوانی متد مذکور را داخل علائم{ }
مینویسیم.
در این مرحله از کار، داخل علائم { }
مربوط به متد ()showSomething
دستوری به منظور نمایش استرینگی با مضمون «اولین متد تعریفشده توسط من» را مینویسیم که برای این منظور کدی به صورت زیر خواهیم داشت:
public class MyClass {
int number = 1;
public void showSomething() {
System.out.println("This is method " + number + " created by me.");
}
}
در واقع، دستور سطر چهارم در وهلۀ اول استرینگ «This is method» را در کنسول چاپ کرده سپس مقدار منتسب به متغیر number
را با آن کانکت کرده و در نهایت مقدار متغیر مذکور نیز با استرینگ «.created by me» کانکت شده و استرینگ نهایی در کنسول چاپ میشود. حال اجرای برنامۀ فوق منجر به بروز اروری به صورت زیر میشود:
Selection does not contain a main type
در واقع، دریافت چنین اروری بدین معنی است که کلاس مورد نظر دارای یک نقطۀ شروع نمیباشد و در آموزشهای پیشین نیز توضیح دادیم که یک برنامۀ نوشتهشده به زبان جاوا برای اجرا نیاز به یک متد ()main
دارد که ساختار این متد به صورت زیر است:
public static void main(String[] args) {
}
در واقع، متد ()main
نقطۀ آغازین برنامه بوده و هر آنچه که در این متد قرار گیرد، به محض اجرای برنامه اجرا میشود. تا به این مرحله از آموزش کلاسی تحت عنوان MyClass
ایجاد کردهایم که میتوانیم از روی آن یک آبجکت بسازیم که برای این منظور هم نیاز داریم تا یک کلاس جدید در پروژۀ OOP ایجاد کرده و در آن متد ()main
را به منزلۀ نقطه آغازین برنامه تعریف کنیم سپس در کلاس جدید خود از روی کلاس MyClass
یک شیئ جدید ایجاد کرده و مورد استفاده قرار دهیم.
برای ایجاد یک کلاس جدید نیز در پروژۀ OOP کلاسی تحت عنوان MySecondClass
در داخل پکیج مربوط به کلاس MyClass
میسازیم که در این مرحله تیک گزینۀ public static void main را نیز میزنیم تا به صورت خودکار متد()main
در این کلاس تعریف شود (توجه داشته باشیم که در حین تعریف کلاس جدید چنانچه پکیج خاصی را مد نظر قرار ندهیم، اکلیپس به طور پیشفرض پکیج مربوط به کلاس پیشین را برای کلاس جدید نیز در نظر میگیرد که بدین ترتیب هر دو کلاس در یک پکیج قرار میگیرند.) حال پس از ایجاد کلاس جدید کدی به صورت زیر خواهیم داشت:
public class MySecondClass {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
همانطور که در آموزشهای پیشین توضیح دادیم، کلیۀ کامنتهای کد فوق قابلحذف بوده و هیچ تأثیری در فرآیند اجرای برنامه ندارند. به علاوه، کلاس فوق بر خلاف کلاس قبل به دلیل دارا بودن متد ()main
قابلیت اجرا داشته و کدهای داخلی آن اجرا میشوند. بنابراین آبجکتی از روی کلاس MyClass
در داخل کلاس فوق میسازیم و برای این منظور کد فوق را به صورت زیر تکمیل میکنیم:
public class MySecondClass {
public static void main(String[] args) {
MyClass newObject = new MyClass();
}
}
همانطور كه میبینیم، ابتدا نام کلاس را ذکر کرده و در ادامه نام آبجکت مد نظر تحت عنوان newObject
را مینویسیم سپس با استفاده از کیود new
آبجکتی جدید از روی کلاس MyClass
ساخته و در انتها علائم ;()
را درج کرده و با استفاده از عملگر =
آبجکت مذکور را به newObject
منتسب مینماییم. اکنون به منظور استفاده از آبجکت ساختهشده از روی کلاس والد، میباید نام آبجکت را نوشته سپس یک .
قرار داده و در نهایت نام متد مورد نظر از کلاس مربوطه را درج نماییم. بنابراین در مثال فوق میتوانیم آبجکت newObject
را نوشته سپس یک علامت .
قرار داده و متد()showSomething
را به صورت زیر فراخوانی کنیم:
public class MySecondClass {
public static void main(String[] args) {
MyClass newObject = new MyClass();
newObject.showSomething();
}
}
با اجرای برنامۀ فوق، آبجکتی جدید از روی کلاس MyClass
تحت عنوان newObject
ساخته شده و بدین ترتیب میتوانیم به کلیۀ اتریبیوتها و متدهای کلاس MyClass
دسترسی داشته باشیم و همانطور که میبينيم در دستور سطر چهارم متد ()showSomething
روی آبجکت ساختهشده از روی کلاس مذکور فراخوانی شده و منجر به چاپ استرینگ زیر در خروجی میگردد:
This is method 1 created by me.
در این مرحله، به کلاس MyClass
بازگشته و تغییری در متد آن ایجاد میکنیم که این تغییر بلافاصله در آبجکت ساختهشده از روی آن کلاس مذکور نیز اِعمال میشود. به طور مثال، استرینگ تعریفشده داخل متد ()println
را به صورت زیر به استرینگ «!This is a change» تغییر میدهیم:
public class MyClass {
int number = 1;
public void showSomething() {
System.out.println("This is a change!");
}
}
پس از سِیو کردن تغییرات اِعمالشده در کلاس MyClass
مجدد به کلاس MySecondClass
بازگشته و آن را اجرا میکنیم که در خروجی خواهیم داشت:
This is a change!
همانطور كه مشاهده میکنیم، به راحتی میتوان در زبان برنامهنویسی جاوا یک آبجکت از روی کلاسی ایجاد کرده و اتریبیوتها و متدهای مرتبط با آن را تغییر داد که این تغییرات بلافاصله در تمامی آبجکتهای ساختهشده از روی کلاس مذکور نیز اِعمال میگردد. دانلود فایلهای تمرین
آشنایی با مفهوم وراثت در زبان برنامهنویسی جاوا
در آموزش قبل نحوۀ ساخت آبجکت از روی کلاس را آموختیم و دیدیم که چگونه میتوان به متدهای کلاس مد نظر دسترسی پیدا کرده و آنها را فراخوانی کرد. حال در این آموزش مفهوم Inheritance یا «وراثت» در زبان برنامهنویسی جاوا را مورد بررسی قرار خواهیم داد.
برخی مواقع در فرآیند توسعۀ نرمافزار چندین آبجکت از روی کلاسهای مختلفی میسازیم که کلاسهای مذکور دارای یکسری خصوصیت مشابه باشند اما این در حالی است که تعریف چندین کلاس با خصوصیات یکسان منجر بدین میشود تا در صورت نیاز به اِعمال تغییر در برخی خصوصیات کلاس مد نظر، مجبور به بازنویسی کلیۀ کلاسها باشیم که در چنین شرایطی سبک برنامهنویسی شیئگرا (OOP) میتواند مشکلات فوقالذکر را رفع نماید بدین صورت که استفاده از آن در برنامهنویسی موجب میشود تا در صورت نیاز به اِعمال تغییر بتوان تغییرات مد نظر را صرفاً در کلاس مربوطه و روی خصوصیات مورد نظر اِعمال نمود.
به طور کلی، ارثبری بدین مفهوم اشاره دارد که کلاسی تعریف کنیم تا کلیۀ ویژگیها و یا برخی از ویژگیهای یک کلاس دیگر را داشته باشد. همچنین از مفهوم وراثت میتوانیم جهت تعریف کلاسی استفاده کنیم که از یکسو تمامی ویژگیهای کلاسی دیگر را داشته و از سوی دیگر برخی ویژگیهای مورد نظر از کلاس اصلی را در آن تغییر دهیم که در ادامۀ آموزشها با نحوۀ انجام این کار نیز آشنا خواهیم شد.
به علاوه، مفهوم دیگری تحت عنوان وراثت سلسله مراتبی نیز داریم که در ادامه با ذکر مثالی به توضیح آن میپردازیم. برای مثال، شرایطی را در نظر میگیریم که سه شیئ داریم به طوری که شیئ شمارۀ دو از شیئ شمارۀ یک و همچنین شیئ شمارۀ سه از شیئ شماره دو ارثبری میکند که در چنین شرایطی شیئ شمارۀ سه از ویژگیهای شیئ شمارۀ دو برخوردار بوده و به دلیل ارثبری شیئ شمارۀ دو از شیئ شمارۀ یک، شیئ سوم ویژگیهای شیئ شمارۀ یک را هم خواهد داشت.
در واقع، در مثال فوق شیئ شمارۀ یک به عنوان Superclass یا «کلاس اصلی» و اشیاء شمارۀ دو و سه نیز به عنوان Subclass یا «کلاس زیرشاخه» تلقی میشوند و جالب است بدانیم که در مثال فوق شیئ شمارۀ دو از یکسو برای شیئ شمارۀ یک به عنوان یک Subclass محسوب میشود و از سوی دیگر برای شیئ شمارۀ سه به عنوان یک Superclass به حساب میآید که چنین رابطهای در تصویر زیر به خوبی قابلمشاهده است:

حال برای آن که درک بهتری از مفهوم وارثت در زبان برنامهنویسی جاوا داشته باشیم، مثالی از دنیای واقعی میزنیم. برای مثال، پدربزرگ خود را در نظر بگیریم که مثلاً دارای یکسری خصوصیات به صورت زیر است:
پدربزرگ | ||
ردیف | عنوان | مقدار |
1 | قد | کوتاه |
2 | رنگ پوست | روشن |
3 | مقدار طاسی | طاس |
4 | خلقوخو | عصبانی |
5 | خلاقیت | بسیارخلاق |
6 | فعالیت | فعال |
7 | ملیت | ایرانی |
همچنین برخی از این خصوصیات در ژن ایشان غالب بوده و به فرزندانشان از جمله پدر نیز انتقال یافته است به طوری که از جمله خصوصیاتی که پدر از وی به ارث برده میتوان به موارد زیر اشاره کرد:
پدر | ||
ردیف | عنوان | مقدار |
1 | قد | کوتاه |
2 | رنگ پوست | روشن |
3 | مقدار طاسی | طاس |
4 | خلقوخو | |
5 | خلاقیت | |
6 | فعالیت | فعال |
7 | ملیت | ایرانی |
8 | سواد | لیسانس |
9 | سطح مطالعه | زیاد |
همانطور که در جدول فوق مشاهده میکنیم، پدر آیتم چهارم را از پدربزرگ به ارث نبرده است. به عبارت دیگر، ایشان خصوصیت عصبانیت را از پدرش به ارث نبرده و فردی خوشاخلاق است. از سوی دیگر، در مورد آیتم پنجم هم خلاقیت پدر به اندازۀ پدربزرگ نمیباشد به طوری که پدر در مقایسه با پدربزرگ از خلاقیت کمتری برخوردار است که این موضوع در برنامهنویسی تحت عنوان Override یا «رونویسی» شناخته میشود.
به عبارت سادهتر، زمانی که میخواهیم تا یک Subclass یا «کلاس زیرشاخه» تعدادی از ویژگیهای Superclass یا «کلاس اصلی» را به ارث نبرد، میتوانيم ویژگیهای مد نظر را به اصطلاح Override کنیم. به علاوه، به خاطر داشته باشیم در شرایطی که یکسری ویژگی هم در Superclass و هم در Subclass موجود باشند، کامپایلر ویژگی موجود در Subclass را مد نظر قرار داده و برنامه را بر اساس آنها اجرا میكند.به خاطر داشته باشیدبرای Override کردن خصوصیات یک کلاس نمیتوانیم اصطلاحاً Modifier متد یا سطح دسترسی تعریفشده برای آن همچون سطوح دسترسی private ،protected و public یا تعداد و نوع پارامترهای ورودی آنها را تغییر دهیم.
علاوه بر خصوصیاتی که پدر آنها را از پدربزرگ به ارث میبرد، برخی از ویژگیهایی را میتوان نام برد که از پدربزرگ به ارث نبرده و خود دارای این ویژگیها میباشد. برای مثال، دو خصیصۀ دیگر همچون موارد هشتم و نهم را میتوان به جدول فوق اضافه کرد که به طور خاص برای پدر بوده و آنها را از پدربزرگ به ارث نبرده است. در واقع، میزان سواد ایشان در سطح لیسانس بوده و از سطح مطالعۀ بالایی نیز برخوردار میباشد. اکنون در جدول زیر برخی از خصوصیاتی را نام میبریم که پسر از پدرِ خود به ارث برده است:
پسر | ||
ردیف | عنوان | مقدار |
1 | قد | |
2 | رنگ پوست | روشن |
3 | مقدار طاسی | |
4 | خلقوخو | عصبانی |
5 | خلاقیت | |
6 | فعالیت | فعال |
7 | ملیت | ایرانی |
8 | سواد | |
9 | سطح مطالعه |
همانطور که در جدول فوق مشاهده میکنیم، پسر در مورد اول بر خلاف پدر و پدربزرگش قدبلند است و در مورد سوم نیز بر خلاف پدر و پدربزرگش طاس نیست. همچنین پسر در مورد چهارم بر خلاف پدرش فردی عصبانی بوده و این خصوصیت را از پدربزرگ خود به ارث برده است و در مورد هشتم نیز پسر بر خلاف پدرش، تحصیلات فوق لیسانس دارد. به علاوه، در مورد نهم سطح مطالعۀ پسر در مقایسه با پدر خیلی زیاد میباشد. اکنون به منظور درک بهتر این روابط، تصویر زیر را مد نظر قرار میدهیم:

همانطور که در تصویر فوق میبینیم، پدربزرگ با رنگ قرمز، پدر با رنگ سبز و پسر با رنگ آبی مشخص شدهاند و در مستطیل مشکی رنگ، خصوصیاتی را میبینیم که بین هر سه نسل مشترک میباشند و در بالای سر هر فرد نیز خصوصیات منحصربهفرد او را مشخص کردهایم. با توجه به آنچه که در این آموزش آموختیم، در آموزش بعد قصد داریم تا به منظور درک بهتر مفهوم وراثت در زبان برنامهنویسی جاوا، از خصوصیات مابین پدربزرگ، پدر و پسر استفاده کرده و بر اساس این خصوصیات پروژهای جدید پیادهسازی نماییم.
بررسی مفهوم وراثت در قالب پروژۀ ارثبری فرزندان از خصوصیات پدر در زبان جاوا
پس از آشنایی با مفهوم وراثت در زبان برنامهنویسی جاوا در آموزش گذشته، در این آموزش قصد داریم تا سناریویی همچون مثال مطرحشده در آموزش قبل را مد نظر قرار داده و آن را پیادهسازی نماییم که برای این منظور پروژهای تحت عنوان 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
میباشد و به همین ترتیب خروجی حاصل از اُورراید کردن متدهای دیگر را مشاهده میکنیم. دانلود فایلهای تمرین
معرفی کلیدواژۀ super در زبان برنامهنویسی جاوا
در این آموزش کلیدواژۀ super
را معرفی میکنیم که از جمله کیوردهای مورد استفاده در وراثت و مباحث مربوط به دسترسی به متغیرها و متدهای تعریفشده در کلاسهای به اصطلاح Superclass یا «کلاسهای والد» میباشد. در واقع، کیورد super
به منظور دسترسی به متدها و متغیرهای کلاس والد در کلاس فرزند مورد استفاده قرار میگیرد (جهت کسب اطلاعات بیشتر در رابطه مفهوم وراثت، توصیه میکنیم به آموزش آشنایی با مفهوم وراثت در زبان برنامهنویسی جاوامراجعه نمایید.)
بهکارگیری کیورد super به منظور دسترسی به متغیرهای کلاس اصلی
در این بخش از آموزش به منظور درک بهتر مطالب ارائهشده، ابتدا پروژهای به نام Super ایجاد کرده و سه کلاس در آن تحت عناوین SubClass
،SuperClass
و ActionClass
میسازیم که کلاس سوم به عنوان نقطۀ شروع برنامه بوده و در حین ساخت آن نیز تیک گزینۀ public static void را میزنیم. در ادامه، کلاس SuperClass
را به صورت زیر تکمیل مینماییم:
public class SuperClass {
int speed = 280;
public void showLexusSpeed() {
System.out.println("Lexus speed is " + speed);
}
}
در کد فوق، داخل کلاس SuperClass
با استفاده از دیتا تایپ int
یک متغیر فیلد تحت عنوان speed
ایجاد کرده و مقدار اولیۀ آن را معادل عدد 280 قرار دادهایم سپس یک متد از جنس void
تحت عنوان ()showLexusSpeed
تعریف کردهایم که این وظیفه را دارا است تا در صورت فراخوانی، مقدار منتسب به متغیر speed
را با استفاده از عملگر +
با استرینگ «Lexus speed is» کانکت کرده و در کنسول چاپ نماید. اکنون کلاس SubClass
را به گونهای پیادهسازی مینماییم تا از کلاس SuperClass
ارثبری کند:
public class SubClass extends SuperClass {
int speed = 260;
public void showToyoTaSpeed() {
System.out.println("Toyota speed is " + speed);
}
}
همانطور که در کد فوق مشاهده میکنیم، کلاس SubClass
از کلاس SuperClass
ارثبری میکند که این کار را با استفاده از کیورد extends
انجام دادهایم سپس با استفاده از دیتا تایپ int
متغیری به نام speed
تعریف کرده و این بار مقدار 260 را بدان اختصاص دادهایم. در ادامه، متدی تحت عنوان ()showToyotaSpeed
در این کلاس تعریف کرده و دستورات داخلی متد مذکور به منظور چاپ مقدار منتسب به متغیر speed
به همراه استرینگ «Toyota speed is» در بخش کنسول را در آن نوشتهایم.
اکنون قصد داریم تا آبجکتی از روی کلاس SubClass
ساخته و بدین ترتیب به متد کلاس مذکور دسترسی داشته و آن را در کلاس ActionClass
روی آبجکت جدید ساختهشده فراخوانی نماییم که برای این منظور کلاس ActionClass
را به صورت زیر تکمیل میکنیم:
public class ActionClass {
public static void main(String[] args) {
SubClass myObject = new SubClass();
myObject.showToyoTaSpeed();
}
}
در کد فوق، آبجکتی از روی کلاس SubClass
به نام myObject
ساخته سپس متد ()showToyotaSpeed
از این کلاس را روی آبجکت myObject
فراخوانی کردهایم به طوری که در خروجی خواهیم داشت:
Toyota speed is 260
همانطور که مشاهده میکنیم، اجرای برنامۀ فوق منجر به فراخوانی متد ()showToyotaSpeed
شده و در نتیجه استرینگ مربوطه در کنسول چاپ میشود. نکتۀ قابلتوجه در ارتباط با متغیر speed
این است که متغیر مذکور هم در SuperClass
و هم در SubClass
تعریف شده است و علیرغم ارثبری کلاس SubClass
از SuperClass
، آبجکت ساختهشده از روی این کلاس متغیر speed
از کلاس فرزند را مد نظر قرار داده و در فراخوانی متد ()showToyotaSpeed
نیز آن را به عنوان آرگومان ورودی به متد مذکور داده است.
حال فرض کنیم اگر بخواهیم تا در فراخوانی متد ()showToyotaSpeed
از کلاس SubClass
مقدار منتسب به متغیرspeed
از کلاس SuperClass
مد نظر قرار داده شده و مقدار آن در کنسول چاپ شود که در چنین شرایطی میتوانیم کیورد super
را به صورت زیر مورد استفاده قرار دهیم:
public class SubClass extends SuperClass {
int speed = 260;
public void showToyoTaSpeed() {
System.out.println("Toyota speed is " + super.speed);
}
}
در کد فوق، کیورد super
را پیش از نام متغیر speed
قرار داده سپس یک علامت .
و در نهایت نام متغیر را نوشتهایم که در این صورت میتوانیم به مقدار منتسب به متغیر speed
از کلاس اصلی دسترسی داشته باشیم به طوری که اگر مجدداً برنامه را اجرا کنیم، در خروجی خواهیم داشت:
Toyota speed is 280
همانطور که ملاحظه میکنیم، درج کیورد super
در کنار نام متغیر منجر بدین شده است تا مقدار منتسب به متغیر مذکور از SuperClass
در متد ()showToyoTaSpeed
متعلق به کلاس SubClass
در نظر گرفته شده و به همراه استرینگ مربوطه در کنسول نمایش داده شود.
بهکارگیری کیورد super به منظور دسترسی به متدهای کلاس اصلی
در این بخش از آموزش قصد داریم تا به بررسی نحوۀ استفاده از کلیدواژۀ super
جهت فراخوانی متدهای کلاس والد در کلاس فرزند بپردازیم که برای درک بهتر مطلب، کد مربوط به کلاس SuperClass
را به صورت زیر بازنویسی میکنیم:
public class SuperClass {
public void showSuperClass() {
System.out.println("This is the SuperClass method");
}
}
در کد فوق، متدی تحت عنوان ()showSuperClass
تعریف نمودهایم که این وظیفه را دارا است تا در صورت فراخوانی، استرینگ «This is the SuperClass method» را در کنسول چاپ نماید. در ادامه، به صورت زیر اقدام به بازنویسی کلاسSubClass
مینماییم:
public class SubClass extends SuperClass {
public void showSubClass() {
System.out.println("This is the SubClass method");
}
}
در کد فوق، متدی تحت عنوان ()showSubClass
تعریف کرده و در آن گفتهایم که در صورت فراخوانی، استرینگ «This is the SubClass method» را در کنسول نمایش دهد و در نهایت کد کلاس ActionClass
را به صورت زیر بازنویسی میکنیم:
public class ActionClass {
public static void main(String[] args) {
SubClass myObject = new SubClass();
myObject.showSubClass();
}
}
در کد فوق، آبجکتی تحت عنوان myObject
از روی کلاس SubClass
ساخته و در ادامه متد ()showSubClass
از کلاس SubClass
را روی آن فراخوانی کردهایم به طوری که پس از اجرای برنامه در خروجی خواهیم داشت:
This is the SubClass method
همانطور که انتظار داشتیم، استرینگ مربوط به کلاس SubClass
در کنسول چاپ شده است. اکنون اگر بخواهیم با استفاده از کلیدواژۀ super
متد ()showSuperClass
از SuperClass
را داخل متد ()showSubClass
از کلاسSubClass
فراخوانی نماییم، میباید کد کلاس SubClass
را به شکل زیر ویرایش نماییم:
public class SubClass extends SuperClass {
public void showSubClass() {
System.out.println("This is the SubClass method");
super.showSuperClass();
}
}
در حقیقت، با نوشتن کلیدواژۀ super
و قرار دادن یک .
سپس نوشتن نام متد ()showSuperClass
از کلاس والد، به متد کلاس مورد نظر در کلاس فرزند دسترسی داشته و آن را فراخوانی مینماییم به طوری که با اجرای برنامۀ فوق در خروجی خواهیم داشت:
This is the SubClass method
This is the SuperClass method
همانطور که مشاهده میکنیم، علاوه بر متد ()showSubClass
از کلاس SubClass
متد ()showSuperClass
از کلاس SuperClass
نیز با استفاده از دستور super
فراخوانی شده و منجر به چاپ استرینگ مربوطه در کنسول شده است. دانلود فایلهای تمرین