آموزش جاوا (فصل ۸)

مقدمه‌ای بر مفهوم شیئ‌گرایی در زبان برنامه‌نویسی جاوا


در این مرحله از آموزش قصد داریم تا نگاه دقیق‌تری به مفاهیم شیئ‌گرایی داشته باشیم که در زبان‌های برنامه‌نویسی متعددی همچون جاوا مورد استفاده قرار می‌گیرند. در حقیقت، شیئ‌گرایی عبارت است از پارادایم‌ها و استراتژی‌هایی که در زبان‌های برنامه‌نویسی بسیاری به کار گرفته می‌شوند.

پایه و اساس رویکرد 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 فراخوانی شده و منجر به چاپ استرینگ مربوطه در کنسول شده است. دانلود فایل‌های تمرین


مطالب مرتبط

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *