مفهوم چندریختی
چند ریختی (polymorphism) در جاوا یعنی یک متغیر آبجکتی، بهجای آبجکت از نوع خودش به یک آبجکت از کلاسی که ازش ارث بری کرده اشاره کنه.
سه اصل شی گرایی عبارتند از کپسوله سازی (Encapsulation)، وراثت (Inheritance) و چند ریختی (Polymorphism).
بعضیا Abstraction هم اصل چهارم شی گرایی میدونن که در اینجا موضوع قابل بحث ما نیست.
در مطالب قبل با دو اصل کپسوله سازی و وراثت اشنا شدیم؛ در این مطلب میخوایم به چند ریختی بپردازیم.
در قسمت مورد مطالعه ی مطلب قبلی یک مثال از کلاس های Circle، Rectangle و GeometricShape زده بودیم.
میتونیم بگیم هر دایره یک شکل هندسیه اما هر شکل هندسی قطعا دایره نیست؛ با این استدلال در مثال قسمت قبل یک متغیر از نوع GeometricShape میتونه به یک آبجکت از نوع Circle اشاره کنه.
حالا میخوایم یک متد عمومی درست کنیم تا اطلاعات هر کلاسی که از GeometricShape ارث میبره رو نمایش بده.
در بالا پارامتر متد displayShapeInfo از نوع GeometricShape است. این متغیر میتونه علاوه بر آبجکت هایی که از GeometricShape هستند به هر آبجکتی که از GeometricShape ارث بری میکنه اشاره کنه.
توجه
هنگامی که با یک متغیر از سوپرکلاس داریم به آدرس یک آبجکت از ساب کلاس اشاره میکنیم، فقط فیلد ها و متد های تعریف شده در سوپرکلاس رو میتونیم صدا بزنیم.
توجه
هر متغیر از نوع سوپرکلاس میتونه به یک آبجکت از ساب کلاس اشاره کنه اما برعکسش ممکن نیست به زبان ساده در مثال بخوایم بیان کنیم هر Circle یک GeometricShape است اما هر GeometricShape همیشه Circle نیست مثلا در بالا میتونه Rectangle باشه.
پیوندپویا (Dynamic Binding)
هنگامی که یک متد طی وراثت، در چند کلاس بازنویسی بشه JVM تصمیم میگیره متد در کدوم کلاس اجرا بشه.
یک متد طی وراثت میتونه در چند ساب کلاس پیادهسازی و بازنویسی بشه.
به نظر شما وقتی یک متغیر از نوع A اشاره کنه به آبجکتی از نوع C چطور باید بدونیم کدوم متد اجرا میشه؟
ابتدا به نوع بیان شده و نوع واقعی می پردازیم.
به متغیری که از نوع سوپر کلاس است و به یک آبجکت از نوع ساب کلاس داره اشاره میکنه نوع بیان شده (declared type) میگیم و به آبجکت اشاره شده از ساب کلاس، نوع واقعی (actual type) میگیم.
فرض کنید یک متغیر از نوع سوپرکلاس داره اشاره میکنه به یک آبجکت از ساب کلاس؛ وقتی یک متد که ابتدا در سوپرکلاس پیادهسازی شده و در ساب کلاس ها طی روند وراثت بازنویسی شده رو صدا میکنیم؛ JVM از actual type شروع میکنه به جستجو و تا declared type به اولین پیادهسازی متد که میرسه اون متد رو به عنوان متد اجرایی در نظر میگیره.
در مثال بالا JVM از C که actual type است شروع میکنه به بررسی میبینه در C بازنویسی متد نداریم میره سراغ B و اونجا متد رو بازنویسی کردیم؛ متدی که در B بازنویسی کردیم رو به کار میگیره و دیگه سراغ بررسی A نمیره.
مثال
در مثال زیر متد toString رو در چند کلاس بازنویسی کردیم؛ زمان اجرا JVM از actual type شروع میکنه به جستجوی اولین پیادهسازی متد toString و بعد از پیدا کردن پیادهسازی متد رو اجرا میکنه و به جستجو ادامه نمیده.
کست کردن (Casting)
کست کردن یعنی تبدیل یک نوع به نوع دیگه.
در مثال قسمت قبل متغیری از نوع Object به Person اشاره کرد و فقط متد های toString، hashCode و equals رو با متغیر میتونستیم صدا بزنیم چون کلاس آبجکت فقط این متد ها داخلش تعریف شده.
این کار کاملا قانونیه چون هر Student یک Object نیز است و بهش کست کردن پنهانی (implicit casting) میگیم.
در کست کردن پنهانی حتما باید actual type ساب کلاس مستقیم یا غیر مستقیم declared type باشه.
یک متغیر از نوع ساب کلاس هم میتونیم به سوپرکلاس کست کنیم؛ در مثال چون هر Object ممکنه به یک Student یا یک Person و یا هر ساب کلاسی که ازش ارث برده باشه اشاره کنه، برای همین باید به صورت آشکار سوپر کلاس رو به ساب کلاس تبدیل کنیم. به این نوع کست، کست کردن آشکار (explicit casting) میگیم.
کلیدواژه ی instanceof
کلیدواژه ی instanceof بررسی میکنه مقدار واقعی (actual type) یک متغیر، چه کلاسی است.
هنگام کست کردن سوپرکلاس به ساب کلاس (explicit casting) اگه متغیری که از نوع سوپرکلاس است به یک آبجکت از ساب کلاس اشاره نکنه زمان اجرا دچار ClassCastException میشیم. در جاوا یک کلیدواژه به نام instanceof وجود داره که قبل از کست کردن میتونیم بررسی کنیم ایا متغیر یک نمونه از ساب کلاس است یا خیر.
مثال:
میتونیم در مورد مطالعه ی مطلب قبل، متد equals رو که ابتدا در کلاس Object تعریف شده در Circle و Rectangle بازنویسی کنیم و داخل متد از instanceof استفاده کنیم.
خلاصه
- سه رکن شی گرایی عبارتند از کپسوله سازی، وراثت و چند ریختی.
- چند ریختی یعنی یک متغیر از نوع سوپرکلاس اشاره کنه به آدرس آبجکت ایجاد شده از ساب کلاس.
- اگه متغیر از نوع سوپر کلاس باشه و به یک آبجکت از نوع ساب کلاس اشاره کنه به متغیر مقدار بیان شده (declared type) و به آبجکتی که بهش اشاره میکنه مقدار واقعی (actual type) میگیم.
- کست کردن یعنی تبدیل نوعی به نوع دیگه.
- با instanceof میتونیم نوع مقدار واقعی یک متغیر رو بررسی کنیم.