JavaRush /وبلاگ جاوا /Random-FA /چند شکلی در جاوا

چند شکلی در جاوا

در گروه منتشر شد
سوالات در مورد OOP بخشی جدایی ناپذیر از یک مصاحبه فنی برای موقعیت یک توسعه دهنده جاوا در یک شرکت فناوری اطلاعات است. در این مقاله در مورد یکی از اصول OOP - چندشکلی صحبت خواهیم کرد. ما بر جنبه‌هایی تمرکز می‌کنیم که اغلب در مصاحبه‌ها از آنها سؤال می‌شود، و همچنین مثال‌های کوچکی برای وضوح ارائه می‌کنیم.

پلی مورفیسم چیست؟

Polymorphism توانایی یک برنامه برای استفاده یکسان از اشیاء با رابط یکسان بدون اطلاعات در مورد نوع خاص این شی است. اگر به این سؤال پاسخ دهید که چندشکلی چیست، به احتمال زیاد از شما خواسته می شود منظورتان را توضیح دهید. یک بار دیگر، بدون پرسیدن یک سری سوال اضافی، همه چیز را برای مصاحبه کننده مرتب کنید.

چند شکلی در جاوا در یک مصاحبه - 1
می‌توانیم با این واقعیت شروع کنیم که رویکرد OOP شامل ساخت یک برنامه جاوا بر اساس تعامل اشیایی است که بر اساس کلاس‌ها هستند. کلاس ها نقشه های از پیش نوشته شده (الگوها) هستند که طبق آنها اشیاء در برنامه ایجاد می شوند. علاوه بر این، یک کلاس همیشه دارای یک نوع خاص است که با یک سبک برنامه نویسی خوب، هدف خود را با نام خود "گفته می کند". علاوه بر این، می توان اشاره کرد که از آنجایی که جاوا یک زبان قوی تایپ شده است، کد برنامه همیشه باید نوع شی را هنگام اعلان متغیرها نشان دهد. به این موارد اضافه کنید که تایپ دقیق ایمنی کد و قابلیت اطمینان برنامه را افزایش می دهد و به شما امکان می دهد از خطاهای ناسازگاری نوع (به عنوان مثال، تلاش برای تقسیم یک رشته بر یک عدد) در مرحله کامپایل جلوگیری کنید. به طور طبیعی، کامپایلر باید نوع اعلام شده را "بشناسد" - این می تواند کلاسی از JDK یا کلاسی باشد که خودمان ایجاد کرده ایم. لطفاً به مصاحبه کننده توجه داشته باشید که هنگام کار با کد برنامه، ما می توانیم نه تنها از اشیایی از نوعی که هنگام اعلام اختصاص داده ایم، بلکه از فرزندان آن نیز استفاده کنیم. این یک نکته مهم است: ما می‌توانیم با بسیاری از انواع به‌گونه‌ای رفتار کنیم که گویی فقط یکی هستند (تا زمانی که آن انواع از یک نوع پایه مشتق شده باشند). این همچنین به این معنی است که با اعلام یک متغیر از نوع سوپرکلاس، می‌توانیم مقدار یکی از فرزندان آن را به آن اختصاص دهیم. اگر مثالی بزنید، مصاحبه کننده از آن خوشش می آید. یک شی را که می تواند برای یک گروه از اشیاء مشترک (پایه) باشد انتخاب کنید و چند کلاس را از آن به ارث ببرید. کلاس پایه:
public class Dancer {
    private String name;
    private int age;

    public Dancer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void dance() {
        System.out.println(toString() + "I dance like everyone else.");
    }

    @Override
    public String toString() {
        return "Я " + name + ", to me " + age + " years. " ;
    }
}
در فرزندان، متد کلاس پایه را لغو کنید:
public class ElectricBoogieDancer extends Dancer {
    public ElectricBoogieDancer(String name, int age) {
        super(name, age);
    }
// overriding the base class method
    @Override
    public void dance() {
        System.out.println( toString() + "I dance electric boogie!");
    }
}

public class BreakDankDancer extends Dancer{

    public BreakDankDancer(String name, int age) {
        super(name, age);
    }
// overriding the base class method
    @Override
    public void dance(){
        System.out.println(toString() + "I'm breakdancing!");
    }
}
مثالی از چندشکلی در جاوا و استفاده از اشیا در یک برنامه:
public class Main {

    public static void main(String[] args) {
        Dancer dancer = new Dancer("Anton", 18);

        Dancer breakDanceDancer = new BreakDankDancer("Alexei", 19);// upcast to base type
        Dancer electricBoogieDancer = new ElectricBoogieDancer("Igor", 20); // upcast to base type

        List<Dancer> discotheque = Arrays.asList(dancer, breakDanceDancer, electricBoogieDancer);
        for (Dancer d : discotheque) {
            d.dance();// polymorphic method call
        }
    }
}
در کد متد آنچه در خطوط وجود دارد mainنشان دهید :
Dancer breakDanceDancer = new BreakDankDancer("Alexei", 19);
Dancer electricBoogieDancer = new ElectricBoogieDancer("Igor", 20);
ما یک متغیر نوع superclass را اعلام کردیم و مقدار یکی از فرزندان را به آن اختصاص دادیم. به احتمال زیاد، از شما پرسیده می شود که چرا کامپایلر از عدم تطابق بین انواع اعلام شده در سمت چپ و سمت راست علامت تخصیص شکایت نمی کند، زیرا جاوا تایپ سختی دارد. توضیح دهید که تبدیل نوع رو به بالا در اینجا کار می کند - ارجاع به یک شی به عنوان ارجاع به کلاس پایه تفسیر می شود. علاوه بر این، کامپایلر با مواجه شدن با چنین ساختاری در کد، این کار را به طور خودکار و ضمنی انجام می دهد. بر اساس کد مثال، می توان نشان داد که نوع کلاس اعلام شده در سمت چپ علامت تخصیص Dancerدارای چندین فرم (نوع) اعلام شده در سمت راست است BreakDankDancer. ElectricBoogieDancerهر یک از اشکال می تواند رفتار منحصر به فرد خود را برای عملکرد مشترک تعریف شده در متد superclass داشته باشد dance. یعنی یک متد اعلام شده در یک سوپرکلاس می تواند به طور متفاوتی در فرزندان آن پیاده سازی شود. در این صورت ما با روش غلبه بر روش سر و کار داریم و این دقیقاً همان چیزی است که انواع اشکال (رفتارها) را ایجاد می کند. با اجرای کد اصلی متد برای اجرا می توانید این را مشاهده کنید: خروجی برنامه من آنتون هستم، 18 سال دارم. من هم مثل بقیه می رقصم. من الکسی هستم، 19 ساله هستم. من بریک رقصم! من ایگور هستم، 20 ساله هستم. من بوگی برقی می رقصم! اگر از overriding در اولاد استفاده نکنیم، رفتار متفاوتی نخواهیم داشت. به عنوان مثال، اگر روش را برای کلاس های خود کامنت BreakDankDancerبگذاریم ، خروجی برنامه به این صورت خواهد بود: من آنتون هستم، من 18 سال دارم. من هم مثل بقیه می رقصم. من الکسی هستم، 19 ساله هستم. من هم مثل بقیه می رقصم. من ایگور هستم، 20 ساله هستم. من هم مثل بقیه می رقصم. و این بدان معنی است که ایجاد کلاس های جدید به سادگی وجود ندارد . اصل چندشکلی جاوا دقیقاً چیست؟ استفاده از یک شی در یک برنامه بدون دانستن نوع خاص آن کجا پنهان است؟ در مثال ما، این یک روش فراخوانی یک شی از نوع است . چند شکلی جاوا به این معنی است که برنامه نیازی به دانستن نوع شی یا شیء ندارد . نکته اصلی این است که از نوادگان این طبقه است . و اگر در مورد فرزندان صحبت کنیم، باید توجه داشت که وراثت در جاوا نه تنها ، بلکه همچنین است . اکنون زمان آن است که به یاد داشته باشیم که جاوا از وراثت چندگانه پشتیبانی نمی کند - هر نوع می تواند یک والد (superclass) و تعداد نامحدودی از فرزندان (زیر کلاس) داشته باشد. بنابراین، اینترفیس ها برای افزودن قابلیت های متعدد به کلاس ها استفاده می شوند. رابط‌ها در مقایسه با وراثت، جفت شدن اشیاء به والدین را کاهش می‌دهند و بسیار مورد استفاده قرار می‌گیرند. در جاوا، یک رابط یک نوع مرجع است، بنابراین یک برنامه می تواند نوع آن را متغیری از نوع رابط اعلام کند. این زمان خوبی برای مثال زدن است. بیایید رابط کاربری را ایجاد کنیم: ElectricBoogieDancerdanceBreakDankDancerElectricBoogieDancerd.dance()dDancerBreakDankDancerElectricBoogieDancerDancerextendsimplements
public interface Swim {
    void swim();
}
برای وضوح، بیایید اشیاء مختلف و نامرتبط را برداریم و یک رابط را در آنها پیاده سازی کنیم:
public class Human implements Swim {
    private String name;
    private int age;

    public Human(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void swim() {
        System.out.println(toString()+"I swim with an inflatable ring.");
    }

    @Override
    public String toString() {
        return "Я " + name + ", to me " + age + " years. ";
    }

}

public class Fish implements Swim{
    private String name;

    public Fish(String name) {
        this.name = name;
    }

    @Override
    public void swim() {
        System.out.println("I'm a fish " + name + ". I swim by moving my fins.");

    }

public class UBoat implements Swim {

    private int speed;

    public UBoat(int speed) {
        this.speed = speed;
    }

    @Override
    public void swim() {
        System.out.println("The submarine is sailing, rotating the propellers, at a speed" + speed + " knots.");
    }
}
روش main:
public class Main {

    public static void main(String[] args) {
        Swim human = new Human("Anton", 6);
        Swim fish = new Fish("whale");
        Swim boat = new UBoat(25);

        List<Swim> swimmers = Arrays.asList(human, fish, boat);
        for (Swim s : swimmers) {
            s.swim();
        }
    }
}
نتیجه اجرای یک روش چند شکلی تعریف شده در یک رابط به ما اجازه می‌دهد تا تفاوت‌هایی را در رفتار انواعی که آن رابط را پیاده‌سازی می‌کنند، ببینیم. آنها شامل نتایج متفاوتی از اجرای روش هستند swim. پس از مطالعه مثال ما، مصاحبه کننده ممکن است در هنگام اجرای کد از علت آن را بپرسدmain
for (Swim s : swimmers) {
            s.swim();
}
آیا متدهای تعریف شده در این کلاس ها برای آبجکت های ما فراخوانی می شوند؟ چگونه پیاده سازی مورد نظر یک متد را هنگام اجرای یک برنامه انتخاب می کنید؟ برای پاسخ به این سوالات باید در مورد صحافی دیرهنگام (پویا) صحبت کنیم. منظور ما از binding ایجاد ارتباط بین فراخوانی متد و پیاده سازی خاص آن در کلاس ها است. اساساً کد تعیین می کند که کدام یک از سه متد تعریف شده در کلاس ها اجرا شود. جاوا به طور پیش فرض از اتصال دیرهنگام استفاده می کند (در زمان اجرا به جای زمان کامپایل، همانطور که در مورد اتصال اولیه وجود دارد). این به این معنی است که هنگام کامپایل کد
for (Swim s : swimmers) {
            s.swim();
}
کامپایلر هنوز نمی داند کد از کدام کلاس است Human، Fishیا اینکه آیا Uboatدر کد اجرا خواهد شد swim. این تنها زمانی مشخص می شود که برنامه به لطف مکانیزم ارسال پویا - بررسی نوع شی در حین اجرای برنامه و انتخاب پیاده سازی مطلوب روش برای این نوع، اجرا شود. اگر از شما پرسیده شود که این چگونه پیاده سازی می شود، می توانید پاسخ دهید که هنگام بارگذاری و مقداردهی اولیه اشیا، JVM جداول را در حافظه می سازد و در آنها متغیرها را با مقادیر آنها و اشیاء را با متدهای آنها مرتبط می کند. علاوه بر این، اگر یک شی به ارث برده شود یا یک رابط را پیاده سازی کند، ابتدا وجود متدهای overrid شده در کلاس آن بررسی می شود. در صورت وجود، آنها به این نوع گره می خورند، اگر نه، متدی جستجو می شود که در کلاس یک سطح بالاتر (در والد) و غیره تا ریشه در یک سلسله مراتب چند سطحی تعریف شده است. در مورد چندشکلی در OOP و پیاده‌سازی آن در کد برنامه، یادآور می‌شویم که استفاده از توصیفات انتزاعی برای تعریف کلاس‌های پایه با استفاده از کلاس‌های انتزاعی و همچنین رابط‌ها، تمرین خوبی است. این عمل مبتنی بر استفاده از انتزاع است - جداسازی رفتار و ویژگی‌های مشترک و محصور کردن آنها در یک کلاس انتزاعی، یا جداسازی تنها رفتار رایج - در این صورت ما یک رابط ایجاد می‌کنیم. ساخت و طراحی سلسله مراتبی از اشیاء بر اساس رابط ها و وراثت کلاس، پیش نیاز تحقق اصل چندشکلی OOP است. با توجه به موضوع چندشکلی و نوآوری در جاوا می توان به این نکته اشاره کرد که هنگام ایجاد کلاس ها و رابط های انتزاعی، با شروع با جاوا 8، می توان پیاده سازی پیش فرض متدهای انتزاعی را در کلاس های پایه با استفاده از کلمه کلیدی نوشت default. مثلا:
public interface Swim {
    default void swim() {
        System.out.println("Just floating");
    }
}
گاهی ممکن است در مورد الزامات اعلام متدها در کلاس های پایه بپرسند تا اصل چندشکلی نقض نشود. همه چیز در اینجا ساده است: این روش ها نباید ثابت ، خصوصی و نهایی باشند . Private متد را فقط در کلاس موجود می کند و شما نمی توانید آن را در decendant لغو کنید. Static متد را به ویژگی کلاس تبدیل می کند نه شیء، بنابراین متد superclass همیشه فراخوانی می شود. Final این روش را تغییرناپذیر و از وارثان خود پنهان می کند.

چندشکلی در جاوا چه چیزی به ما می دهد؟

به احتمال زیاد این سوال نیز مطرح خواهد شد که استفاده از چندشکلی چه چیزی به ما می دهد. در اینجا می توانید به طور خلاصه پاسخ دهید، بدون اینکه خیلی عمیق به علف های هرز بروید:
  1. به شما امکان می دهد پیاده سازی های شی را جایگزین کنید. این چیزی است که آزمایش بر اساس آن است.
  2. قابلیت توسعه برنامه را فراهم می کند - ایجاد پایه ای برای آینده بسیار آسان تر می شود. افزودن انواع جدید بر اساس انواع موجود رایج ترین راه برای گسترش عملکرد برنامه های نوشته شده به سبک OOP است.
  3. به شما امکان می دهد اشیاء را با یک نوع یا رفتار رایج در یک مجموعه یا آرایه ترکیب کنید و آنها را به طور یکنواخت مدیریت کنید (مانند نمونه های ما، وادار کردن همه به رقص - یک روش danceیا شنا - یک روش swim).
  4. انعطاف‌پذیری هنگام ایجاد انواع جدید: می‌توانید روشی را از والدین اجرا کنید یا آن را در فرزند نادیده بگیرید.

کلمات جدایی برای سفر

اصل چندشکلی موضوع بسیار مهم و گسترده ای است. تقریباً نیمی از OOP جاوا و بخش خوبی از اصول اولیه زبان را پوشش می دهد. شما نمی توانید از تعریف این اصل در مصاحبه خلاص شوید. ناآگاهی یا درک نادرست از آن به احتمال زیاد به مصاحبه پایان می دهد. بنابراین، برای بررسی دانش خود قبل از آزمون تنبلی نکنید و در صورت لزوم آن را تجدید کنید.
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION