مشخصات مقاله
آموزش Java – Polymorphism یا چندریختی در Java
آموزش Java – Polymorphism یا چندریختی در Java
polymorphism یا چندریختی مفهومی مشترک بین زبان های شی گرا است که طی آن می توان یک فعل یا عملیات خاص را به روش های مختلف انجام داد. در زبان برنامه نویسی جاوا وقتی سخن از یک تابع چندریختی به میان می آید، منظور قابلیت و توانایی آن تابع برای انجام کارهای مختلف بر اساس آبجکتی است که به آن متعلق است.
خود واژه ی polymorphism از دو کلمه ی یونانی poly و morph تشکیل شده که هر یک به ترتیب به معنای چند و شکل می باشند. بنابراین polymorphism در کنار هم به معنای چندشکلی یا چندریختی می باشد و به معنای ساده تر هر چیز که بتواند اشکال مختلف به خود بگیرد.
در جاوا دو نوع polymorphism وجود دارد: 1. polymorphism در زمان کامپایل 2. polymorphism در زمان اجرا. در زبان جاوا با فراخوانی متدی یکسان با پارامترهای مختلف (overloading) و بازنویسی بدنه ی متد (overriding) در کلاس فرزند، می توان مفهوم polymorphism را به آسانی پیاده سازی کرد.
اگر توسعه دهنده متدی static را با پارامترهای مختلف صدا بزند/overload کند، در واقع polymorphism زمان کامپایل را پیاده سازی کرده است.
ما قصد داریم در این مبحث polymorphism زمان اجرا را پیاده سازی کنیم.
شرح مفهوم Upcasting (تبدیل کلاس سطح پایین تر یا فرزند به کلاس سطح بالاتر یا پدر)
زمانی که آّبجکتی (reference variable) از کلاس پدر آبجکتی از کلاس فرزند را در خود نگه داشته و به آن اشاره کند، در برنامه نویسی شی گرا به آن upcasting می گویند. مثال:

1 2 3 4 | class A{} class B extends A{} A a= new B(); //upcasting <button></button> |
مثال کاربردی از پیاده سازی مفهوم polymorphism در زمان اجرا
در مثال پیشرو دو کلاس به نام های Bike و Splendar ایجاد می کنیم. Splendar از کلاس Bike ارث بری کرده و متد run() آن را داخل خود بازنویسی (override) می کند. همان طور که در زیر مشاهده می کنید، متد run() را از طریق متغیری از جنس کلاس پدر (Bike) یا متغیر اشاره گر به کلاس پدر فراخوانی می کنیم. از آنجایی که متغیر b از جنس کلاس پدر (Bike) نمونه ای از کلاس Splender را در خود نگه داشته و متد run() داخل کلاس فرزند (Splender) بازنویسی شده است، متد کلاس فرزند در زمان اجرای برنامه فراخوانی می شود.
همچنین به این خاطر که jvm (دستگاه مجاری جاوا که بستر اجرای برنامه را فراهم می کند) فراخوانی متدها را انجام می دهد، نه کامپایلر، به این نوع پیاده سازی runtime polymorphism یا پیاده سازی مفهوم چندریختی در زمان اجرای برنامه می گویند.
1 2 3 4 5 6 7 8 9 10 11 | class Bike{ void run(){System.out.println( "running" );} } class Splender extends Bike{ void run(){System.out.println( "running safely with 60km" );} public static void main(String args[]){ Bike b = new Splender(); //upcasting b.run(); } } <button></button> |
خروجی:
1 | running safely with 60km.<button></button> |
پیاده سازی polymorphism در زمان اجرا: مثال Bank
¬سناریویی را در نظر بگیرید که در آن Bank یک کلاس ساده است که نرخ پرداخت سود ماهیانه را خروجی می دهد. نرخ پرداخت سود ممکن است با توجه به بانک (آبجکت) مربوطه متفاوت باشد. برای مثال بانک های SBI، ICICI و AXIS هر یک به ترتیب مقادیر 8.4%، 7.3% و 9.7% را به عنوان نرخ پرداخت سود ماهیانه درنظر می گیرند.

این مثال در مبحث overriding نیز پوشش داده شده است با این تفاوت که در آن هیچ upcasting و تبدیل از کلاس فرزند به پدر پیاده سازی نمی شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class Bank{ float getRateOfInterest(){ return 0;} } class SBI extends Bank{ float getRateOfInterest(){ return 8.4f;} } class ICICI extends Bank{ float getRateOfInterest(){ return 7.3f;} } class AXIS extends Bank{ float getRateOfInterest(){ return 9.7f;} } class TestPolymorphism{ public static void main(String args[]){ Bank b; b= new SBI(); System.out.println( "SBI Rate of Interest: " +b.getRateOfInterest()); b= new ICICI(); System.out.println( "ICICI Rate of Interest: " +b.getRateOfInterest()); b= new AXIS(); System.out.println( "AXIS Rate of Interest: " +b.getRateOfInterest()); } } <button></button> |
خروجی:
1 2 3 4 | SBI Rate of Interest: 8.4 ICICI Rate of Interest: 7.3 AXIS Rate of Interest: 9.7 <button></button> |
پیاده سازی polymorphism در زمان اجرا: مثال Shape
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class Shape{ void draw(){System.out.println( "drawing..." );} } class Rectangle extends Shape{ void draw(){System.out.println( "drawing rectangle..." );} } class Circle extends Shape{ void draw(){System.out.println( "drawing circle..." );} } class Triangle extends Shape{ void draw(){System.out.println( "drawing triangle..." );} } class TestPolymorphism2{ public static void main(String args[]){ Shape s; s= new Rectangle(); s.draw(); s= new Circle(); s.draw(); s= new Triangle(); s.draw(); } } <button></button> |
خروجی:
1 2 3 4 | drawing rectangle... drawing circle... drawing triangle... <button></button> |
پیاده سازی polymorphism در زمان اجرا: مثال Animal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Animal{ void eat(){System.out.println( "eating..." );} } class Dog extends Animal{ void eat(){System.out.println( "eating bread..." );} } class Cat extends Animal{ void eat(){System.out.println( "eating rat..." );} } class Lion extends Animal{ void eat(){System.out.println( "eating meat..." );} } class TestPolymorphism3{ public static void main(String[] args){ Animal a; a= new Dog(); a.eat(); a= new Cat(); a.eat(); a= new Lion(); a.eat(); }} <button></button> |
خروجی:
1 2 3 4 | eating bread... eating rat... eating meat... <button></button> |
پیاده سازی polymorphism در زمان اجرا با فیلدها و متغیرهای کلاس/ Data Member
این متد است که بدنه ی آن داخل کلاس فرزند بازنویسی می شود. فیلدهای یک کلاس قابل بازنویسی نیستند و نمی توان polymorphism زمان اجرا را بر روی آن ها پیاده سازی کرد.
در مثال زیر فیلد speedlimit بین دو کلاس پدر و فرزند مشترک بوده ولی در هر کلاس دارای مقادیر متفاوت می باشد. همان طور که مشاهده می کنید از طریق آبجکت کلاس پدر (متغیر اشاره گر به کلاس پدر یا همان b) که نمونه ی کلاس فرزند را در خود نگه می دارد، به فیلد مورد نظر از کلاس فرزند (speedlimit=150) دسترسی پیدا کرده و آن را فراخوانی می کنیم. از آنجایی که در این مثال متغیر کلاس فرزند را فراخوانی می کنیم که بازنویسی نشده است، برنامه مقدار کلاس پدر را در خروجی چاپ می کند.
runtime polymorphism با اعضای داده ای کلاس یا همان فیلدها قابل پیاده سازی نمی باشد.
1 2 3 4 5 6 7 8 9 10 | class Bike{ int speedlimit=90; } class Honda3 extends Bike{ int speedlimit=150; public static void main(String args[]){ Bike obj= new Honda3(); System.out.println(obj.speedlimit); //90 } <button></button> |
خروجی:
1 | 90<button></button> |
پیاده سازی polymorphism در زمان اجرای برنامه با وراثت چند سطحی (multi-level)
در زیر مثال ساده ای که polymorphism در زمان اجرا با وراثت چند سطحی پیاده سازی شده را مشاهده می کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Animal{ void eat(){System.out.println( "eating" );} } class Dog extends Animal{ void eat(){System.out.println( "eating fruits" );} } class BabyDog extends Dog{ void eat(){System.out.println( "drinking milk" );} public static void main(String args[]){ Animal a1,a2,a3; a1= new Animal(); a2= new Dog(); a3= new BabyDog(); a1.eat(); a2.eat(); a3.eat(); } } <button></button> |
خروجی:
1 2 3 4 | eating eating fruits drinking Milk <button></button> |
مثال کاربردی نهایی
1 2 3 4 5 6 7 8 9 10 11 12 | class Animal{ void eat(){System.out.println( "animal is eating..." );} } class Dog extends Animal{ void eat(){System.out.println( "dog is eating..." );} } class BabyDog1 extends Dog{ public static void main(String args[]){ Animal a= new BabyDog1(); a.eat(); }} <button></button> |
خروجی:
1 | Dog is eating<button></button> |
در مثال حاضر، همان طور که مشاهده می کنید از آنجایی که کلاس فرزند BabyDog متد eat() از کلاس پدر (Dog) را در بدنه ی خود بازنویسی (override) نمی کند، متد eat() کلاس Dog صدا خورده شده و متن Dog is eating در خروجی چاپ می شود.