چند ریختی (Polymorphism) و کست کردن در جاوا

چند ریختی و کست کردن در جاوا

مفهوم چندریختی

چند ریختی (polymorphism) در جاوا یعنی یک متغیر آبجکتی، به‌جای آبجکت از نوع خودش به یک آبجکت از کلاسی که ازش ارث بری کرده اشاره کنه.

سه اصل شی گرایی عبارتند از کپسوله سازی (Encapsulation)، وراثت (Inheritance) و چند ریختی (Polymorphism).

بعضیا Abstraction هم اصل چهارم شی گرایی میدونن که در اینجا موضوع قابل بحث ما نیست.

در مطالب قبل با دو اصل کپسوله سازی و وراثت اشنا شدیم؛ در این مطلب میخوایم به چند ریختی بپردازیم.

در قسمت مورد مطالعه ی مطلب قبلی یک مثال از کلاس های Circle، Rectangle و GeometricShape زده بودیم.

میتونیم بگیم هر دایره یک شکل هندسیه اما هر شکل هندسی قطعا دایره نیست؛ با این استدلال در مثال قسمت قبل یک متغیر از نوع GeometricShape میتونه به یک آبجکت از نوع Circle اشاره کنه.

حالا میخوایم یک متد عمومی درست کنیم تا اطلاعات هر کلاسی که از GeometricShape ارث میبره رو نمایش بده.

public static void main(String args){ displayShapeInfo(new Circle(4, "Blue", true); displayShapeInfo(new Rectangle(7, 6, "Yellow", true)); } public static void displayShapeInfo(GeometricShape shape){ System.out.println("Created on " + shape.getDateCreated() + "\nThe color is " + shape.getColor() + "\nIs it filled? " + shape.isFilled()); }

در بالا پارامتر متد displayShapeInfo از نوع GeometricShape است. این متغیر میتونه علاوه بر آبجکت هایی که از GeometricShape هستند به هر آبجکتی که از GeometricShape ارث بری میکنه اشاره کنه.

توجه

هنگامی که با یک متغیر از سوپرکلاس داریم به آدرس یک آبجکت از ساب کلاس اشاره میکنیم، فقط فیلد ها و متد های تعریف شده در سوپرکلاس رو میتونیم صدا بزنیم.

توجه

هر متغیر از نوع سوپرکلاس میتونه به یک آبجکت از ساب کلاس اشاره کنه اما برعکسش ممکن نیست به زبان ساده در مثال بخوایم بیان کنیم هر Circle یک GeometricShape است اما هر GeometricShape همیشه Circle نیست مثلا در بالا میتونه Rectangle باشه.

پیوندپویا (Dynamic Binding)

هنگامی که یک متد طی وراثت، در چند کلاس بازنویسی بشه JVM تصمیم میگیره متد در کدوم کلاس اجرا بشه.

یک متد طی وراثت میتونه در چند ساب کلاس پیاده‌سازی و بازنویسی بشه.

class A { String myMethod(){ return "From A"; } } class B extends A { @Override String myMethod(){ return "From B"; } } class C extends B{ }
public static void main(String[] args){ A o = new C(); System.out.println(o.myMethod()); }

به نظر شما وقتی یک متغیر از نوع A اشاره کنه به آبجکتی از نوع C چطور باید بدونیم کدوم متد اجرا میشه؟

ابتدا به نوع بیان شده و نوع واقعی می پردازیم.

به متغیری که از نوع سوپر کلاس است و به یک آبجکت از نوع ساب کلاس داره اشاره میکنه نوع بیان شده (declared type) میگیم و به آبجکت اشاره شده از ساب کلاس، نوع واقعی (actual type) میگیم.

فرض کنید یک متغیر از نوع سوپرکلاس داره اشاره میکنه به یک آبجکت از ساب کلاس؛ وقتی یک متد که ابتدا در سوپرکلاس پیاده‌سازی شده و در ساب کلاس ها طی روند وراثت بازنویسی شده رو صدا میکنیم؛ JVM از actual type شروع میکنه به جستجو و تا declared type به اولین پیاده‌سازی متد که میرسه اون متد رو به عنوان متد اجرایی در نظر میگیره.

در مثال بالا JVM از C که actual type است شروع میکنه به بررسی میبینه در C بازنویسی متد نداریم میره سراغ B و اونجا متد رو بازنویسی کردیم؛ متدی که در B بازنویسی کردیم رو به کار میگیره و دیگه سراغ بررسی A نمیره.

مثال

در مثال زیر متد toString رو در چند کلاس بازنویسی کردیم؛ زمان اجرا JVM از actual type شروع میکنه به جستجوی اولین پیاده‌سازی متد toString و بعد از پیدا کردن پیاده‌سازی متد رو اجرا میکنه و به جستجو ادامه نمیده.

public class DynamicBindingDemo { public static void main(String[] args) { Object graduateStudent = new GraduateStudent(); Object Student = new Student(); Object person = new Person(); Object object = new Object(); System.out.println(graduateStudent.toString()); System.out.println(student.toString()); System.out.println(person.toString()); System.out.println(object.toString()); } } class GraduateStudent extends Student { } class Student extends Person { @Override public String toString() { return "Student"; } } class Person extends Object { @Override public String toString() { return "Person"; } }

کست کردن (Casting)

کست کردن یعنی تبدیل یک نوع به نوع دیگه.

در مثال قسمت قبل متغیری از نوع Object به Person اشاره کرد و فقط متد های toString، hashCode و equals رو با متغیر میتونستیم صدا بزنیم چون کلاس آبجکت فقط این متد ها داخلش تعریف شده.

Object declared = new Student();

این کار کاملا قانونیه چون هر Student یک Object نیز است و بهش کست کردن پنهانی (implicit casting) میگیم.

در کست کردن پنهانی حتما باید actual type ساب کلاس مستقیم یا غیر مستقیم declared type باشه.

یک متغیر از نوع ساب کلاس هم میتونیم به سوپرکلاس کست کنیم؛ در مثال چون هر Object ممکنه به یک Student یا یک Person و یا هر ساب کلاسی که ازش ارث برده باشه اشاره کنه، برای همین باید به صورت آشکار سوپر کلاس رو به ساب کلاس تبدیل کنیم. به این نوع کست، کست کردن آشکار (explicit casting) میگیم.

Student student = (Student) declared;

کلیدواژه ی instanceof

کلیدواژه ی instanceof بررسی میکنه مقدار واقعی (actual type) یک متغیر، چه کلاسی است.

هنگام کست کردن سوپرکلاس به ساب کلاس (explicit casting) اگه متغیری که از نوع سوپرکلاس است به یک آبجکت از ساب کلاس اشاره نکنه زمان اجرا دچار ClassCastException میشیم. در جاوا یک کلیدواژه به نام instanceof وجود داره که قبل از کست کردن میتونیم بررسی کنیم ایا متغیر یک نمونه از ساب کلاس است یا خیر.

GeometricShape o = new Circle(); System.out.println("Is o instance of Circle? "+ o instanceof Circle); System.out.println("Is o instance of Rectangle? " + o instanceof Rectangle);

مثال:

میتونیم در مورد مطالعه ی مطلب قبل، متد equals رو که ابتدا در کلاس Object تعریف شده در Circle و Rectangle بازنویسی کنیم و داخل متد از instanceof استفاده کنیم.

class GeometricShape { ... } class Circle extends GeometricShape{ ... @Override public boolean equals(Object obj) { if (obj instanceof Circle) return ((Circle) obj).radius == this.radius; return false; } } class Rectangle extends GeometricShape{ ... @Override public boolean equals(Object obj) { if (obj instanceof Rectangle){ Rectangle r = (Rectangle) obj; return r.width == this.width && r.height == this.height; } return false; }

خلاصه

- سه رکن شی گرایی عبارتند از کپسوله سازی، وراثت و چند ریختی.

- چند ریختی یعنی یک متغیر از نوع سوپرکلاس اشاره کنه به آدرس آبجکت ایجاد شده از ساب کلاس.

- اگه متغیر از نوع سوپر کلاس باشه و به یک آبجکت از نوع ساب کلاس اشاره کنه به متغیر مقدار بیان شده (declared type) و به آبجکتی که بهش اشاره میکنه مقدار واقعی (actual type) میگیم.

- کست کردن یعنی تبدیل نوعی به نوع دیگه.

- با instanceof میتونیم نوع مقدار واقعی یک متغیر رو بررسی کنیم.

arrow_drop_up
کپی شد!