۳۰ آذر ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

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

با چند شکلی در جاوا و نحوه فراخوانی متد در فراخوانی متد چند شکلی شروع کنید.

با چند شکلی در جاوا و نحوه فراخوانی متد در فراخوانی متد چند شکلی شروع کنید.

Polymorphism – یا توانایی یک شی برای اجرای اقدامات تخصصی بر اساس نوع آن – چیزی است که کد جاوا را انعطاف پذیر می کند. بسیاری از الگوهای طراحی ایجاد شده توسط گروه چهار بر نوعی از چندشکلی تکیه دارند، از جمله الگوی فرمان. در این مقاله با اصول چندشکلی جاوا و نحوه استفاده از آن در برنامه های خود آشنا خواهید شد.

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

  • چند شکلی و وراثت جاوا
  • چرا چندشکلی مهم است
  • چند شکلی در غلبه بر روش
  • چند شکلی با کلاس های اصلی جاوا
  • فراخوانی و ریخته گری روش چند شکل
  • کلمات کلیدی رزرو شده و چند شکلی
  • اشتباهات رایج چندشکلی
  • چه چیزهایی را باید در مورد چندشکلی به خاطر بسپارید

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

ما بر رابطه بین چندشکلی و وراثت جاوا تمرکز خواهیم کرد. نکته اصلی که باید در نظر داشت این است که چندشکلی نیاز به وراثت یا پیاده سازی رابط دارد. شما می توانید این را در مثال زیر مشاهده کنید که شامل دوک و جوگی است:


public abstract class JavaMascot {
    public abstract void executeAction();
}
public class Duke extends JavaMascot {
    @Override
    public void executeAction() {
        System.out.println("Punch!");
    }
}
public class Juggy extends JavaMascot {
    @Override
    public void executeAction() {
        System.out.println("Fly!");
    }
}
public class JavaMascotTest {
    public static void main(String... args) {
        JavaMascot dukeMascot = new Duke();
        JavaMascot juggyMascot = new Juggy();
        dukeMascot.executeAction();
        juggyMascot.executeAction();
    }
}

خروجی این کد خواهد بود:

 
Punch!
Fly!

به دلیل اجرای خاص آنها، هم اقدامات Duke و هم Juggy اجرا خواهند شد.

چرا چندشکلی مهم است

هدف از استفاده از چند شکلی جدا کردن کلاس کلاینت از کد پیاده سازی است. کلاس کلاینت به جای کدگذاری سخت، پیاده سازی را برای اجرای اقدام لازم دریافت می کند. به این ترتیب، کلاس کلاینت به اندازه کافی می داند که اقدامات خود را انجام دهد، که نمونه ای از اتصال شل است.

برای درک بهتر مزایای چندشکلی، به SweetCreator نگاهی بیندازید:


public abstract class SweetProducer {
    public abstract void produceSweet();
}
public class CakeProducer extends SweetProducer {
    @Override
    public void produceSweet() {
        System.out.println("Cake produced");
    }
}
public class ChocolateProducer extends SweetProducer {
    @Override
    public void produceSweet() {
        System.out.println("Chocolate produced");
    }
}
public class CookieProducer extends SweetProducer {
    @Override
    public void produceSweet() {
        System.out.println("Cookie produced");
    }
}
public class SweetCreator {
    private List<SweetProducer> sweetProducer;
    public SweetCreator(List<SweetProducer> sweetProducer) {
        this.sweetProducer = sweetProducer;
    }
    public void createSweets() {
        sweetProducer.forEach(sweet -> sweet.produceSweet());
    }
}
public class SweetCreatorTest {
    public static void main(String... args) {
        SweetCreator sweetCreator = new SweetCreator(Arrays.asList(new CakeProducer(),
                new ChocolateProducer(), new CookieProducer()));
        sweetCreator.createSweets();
    }
}

در این مثال، می‌توانید ببینید که کلاس SweetCreator فقط کلاس SweetProducer را می‌شناسد. اجرای هر Sweet را نمی داند. این جداسازی به ما انعطاف‌پذیری برای به‌روزرسانی و استفاده مجدد از کلاس‌هایمان می‌دهد و حفظ کد را بسیار آسان‌تر می‌کند. هنگام طراحی کد خود، همیشه به دنبال راه هایی باشید تا آن را تا حد امکان انعطاف پذیر و قابل نگهداری کنید. Polymorphism یک تکنیک بسیار قدرتمند برای نوشتن کد جاوا قابل استفاده مجدد است.

پروژه Valhalla بهبودهای مدل شی جاوا را ارائه می کند

نکته: حاشیه نویسی @Override برنامه نویس را ملزم می کند که از امضای روشی مشابهی استفاده کند که باید لغو شود. اگر روش لغو نشود، یک خطای کامپایل رخ خواهد داد.

آیا روش اضافه بار چندشکلی است؟

بسیاری از برنامه نویسان در مورد ارتباط چندشکلی با روش و روش بازگردانی اشتباه دارند. اضافه بار. با این حال، تنها روش نادیده گرفته شده، چندشکلی واقعی است. Overloading نام روش یکسانی را به اشتراک می گذارد اما پارامترها متفاوت هستند. چند شکلی یک اصطلاح گسترده است، بنابراین همیشه بحث هایی در مورد این موضوع وجود دارد،

چند شکلی در غلبه بر روش

این امکان وجود دارد که نوع برگشتی یک روش لغو شده را تغییر دهید اگر از نوع کوواریانت باشد. یک نوع کوواریانت اساساً یک زیر کلاس از نوع بازگشتی است. این یک مثال است:


public abstract class JavaMascot {
    abstract JavaMascot getMascot();
}
public class Duke extends JavaMascot {
    @Override
    Duke getMascot() {
        return new Duke();
    }
}

از آنجایی که Duke یک JavaMascot است، می‌توانیم نوع بازگشت را هنگام لغو تغییر دهیم.

چند شکلی با کلاس های اصلی جاوا

ما همیشه از چندشکلی در کلاس‌های اصلی جاوا استفاده می‌کنیم. یک مثال بسیار ساده زمانی است که کلاس ArrayList را نمونه سازی می کنیم و رابط  List را به عنوان یک نوع اعلام می کنیم:


List<String> list = new ArrayList<>();

برای ادامه، این نمونه کد را با استفاده از Java Collections API بدون چندشکلی در نظر بگیرید:


public class ListActionWithoutPolymorphism {
   // Example without polymorphism
    void executeVectorActions(Vector<Object> vector) {/* Code repetition here*/}
    void executeArrayListActions(ArrayList<Object> arrayList) {/*Code repetition here*/}
    void executeLinkedListActions(LinkedList<Object> linkedList) {/* Code repetition here*/}
    void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList<Object> copyOnWriteArrayList)
        { /* Code repetition here*/}
}
public class ListActionInvokerWithoutPolymorphism {
        listAction.executeVectorActions(new Vector<>());
        listAction.executeArrayListActions(new ArrayList<>());
        listAction.executeLinkedListActions(new LinkedList<>());
        listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList<>());
}

این کد زشتی است، اینطور نیست؟ تصور کنید سعی می کنید آن را حفظ کنید! اکنون به همان مثال با چندشکلی نگاه کنید:


public static void main(String … polymorphism) {
ListAction listAction = new ListAction();	
	listAction.executeListActions();
}
public class ListAction {
    void executeListActions(List<Object> list) {
        // Execute actions with different lists
    }
}
public class ListActionInvoker {
    public static void main(String... masterPolymorphism) {
        ListAction listAction = new ListAction();
        listAction.executeListActions(new Vector<>());
        listAction.executeListActions(new ArrayList<>());
        listAction.executeListActions(new LinkedList<>());
        listAction.executeListActions(new CopyOnWriteArrayList<>());
    }
}

مزایای پلی مورفیسم انعطاف پذیری و گسترش پذیری است. به جای ایجاد چندین روش مختلف، می‌توانیم فقط یک روش را اعلام کنیم که نوع عمومی List را دریافت می‌کند.

فراخوانی و ریخته گری روش چند شکل

امکان فراخوانی روش‌های خاصی در یک فراخوانی چند شکلی وجود دارد، اما انجام آن به قیمت انعطاف‌پذیری تمام می‌شود. این یک مثال است:


public abstract class MetalGearCharacter {
    abstract void useWeapon(String weapon);
}
public class BigBoss extends MetalGearCharacter {
    @Override
    void useWeapon(String weapon) {
        System.out.println("Big Boss is using a " + weapon);
    }
   void giveOrderToTheArmy(String orderMessage) {
        System.out.println(orderMessage);
    }
}
public class SolidSnake extends MetalGearCharacter {
    void useWeapon(String weapon) {
        System.out.println("Solid Snake is using a " + weapon);
    }
}
public class UseSpecificMethod {
    public static void executeActionWith(MetalGearCharacter metalGearCharacter) {
        metalGearCharacter.useWeapon("SOCOM");
	// The below line wouldn't work
        // metalGearCharacter.giveOrderToTheArmy("Attack!");
        if (metalGearCharacter instanceof BigBoss) {
            ((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!");
        }
    }
public static void main(String... specificPolymorphismInvocation) {
        executeActionWith(new SolidSnake());
        executeActionWith(new BigBoss());
    }
}

تکنیکی که ما در اینجا استفاده می‌کنیم، casting یا تغییر عمدی نوع شی در زمان اجرا است.

CUDA چیست؟ برنامه نویسی موازی برای پردازنده های گرافیکی

توجه داشته باشید که هنگام فرستادن نوع عمومی به نوع خاص، می‌توان یک روش خاص را فقط فراخوانی کرد. یک قیاس خوب این است که به صراحت به کامپایلر بگوییم: “هی، من می دانم که اینجا چه کار می کنم، بنابراین می خواهم شی را به یک نوع خاص تبدیل کنم و از یک روش خاص استفاده کنم.”

با اشاره به مثال بالا، دلیل مهمی وجود دارد که کامپایلر از پذیرش فراخوانی متد خاص خودداری می کند: کلاسی که در حال ارسال است می تواند SolidSnake باشد. در این مورد، هیچ راهی برای کامپایلر وجود ندارد که مطمئن شود هر زیر کلاس MetalGearCharacter دارای روش giveOrderToTheArmy اعلام شده است.

دریافت کد: کد منبع را برای این چالش دریافت کنید و تست های خود را اجرا کنید.

کلیدواژه های رزرو شده

به کلمه رزرو شده instanceof توجه کنید. قبل از فراخوانی روش خاص، ما پرسیدیم که آیا MetalGearCharacterinstanceofBigBoss است یا خیر. اگر یک نمونه BigBoss نبود، پیام استثنایی زیر را دریافت می‌کنیم:


Exception in thread "main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake cannot be cast to com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss

اگر بخواهیم به یک ویژگی یا متد از یک سوپرکلاس جاوا ارجاع دهیم چه؟ در این مورد، می توانیم از کلمه رزرو شده super استفاده کنیم. به عنوان مثال:


public class JavaMascot {
  void executeAction() {
    System.out.println("The Java Mascot is about to execute an action!");
  }
}
public class Duke extends JavaMascot {
  @Override
  void executeAction() {
    super.executeAction();
    System.out.println("Duke is going to punch!");
  }
  public static void main(String... superReservedWord) {
    new Duke().executeAction();
  }
}

استفاده از کلمه رزرو شده super در روش executeAction Duke، متد superclass را فراخوانی می‌کند. سپس اقدام خاص را از Duke اجرا می کنیم. به همین دلیل است که می توانیم هر دو پیام را در خروجی زیر مشاهده کنیم:


The Java Mascot is about to execute an action!
Duke is going to punch!

اشتباهات رایج چندشکلی

این یک اشتباه رایج است که فکر کنیم می‌توان روش خاصی را بدون استفاده از ریخته‌گری فراخوانی کرد.

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

همچنین به یاد داشته باشید که نادیده گرفتن روش، بارگذاری بیش از حد روش نیست.

اگر پارامترها متفاوت باشند، نادیده گرفتن یک روش غیرممکن است. اگر نوع برگشتی زیر کلاس متد superclass باشد، ممکن است نوع برگشتی روش لغو شده را تغییر دهید.

چه چیزی را در مورد چندشکلی به خاطر بسپارید

  • نمونه ایجاد شده تعیین می کند که هنگام استفاده از چندشکلی از چه روشی فراخوانی می شود.
  • حاشیه نویسی @Override برنامه نویس را ملزم به استفاده از یک روش لغو می کند. اگر نه، یک خطای کامپایلر وجود خواهد داشت.
  • چند ریختی را می توان با کلاس های عادی، کلاس های انتزاعی و رابط ها استفاده کرد.
  • بیشتر الگوهای طراحی به نوعی از چندشکلی بستگی دارند.
  • تنها راه برای استفاده از یک روش خاص در زیر کلاس چند شکلی استفاده از ریخته‌گری است.
  • این امکان وجود دارد که یک ساختار قدرتمند در کد خود با استفاده از چند شکلی طراحی کنید.

چالش چندشکلی را قبول کنید!

بیایید آنچه را که در مورد چندشکلی و وراثت آموخته‌اید امتحان کنیم. در این چالش، چند روش از سیمپسون‌ها مت گرونینگ به شما داده می‌شود، و چالش شما این است که نتیجه هر کلاس را استنباط کنید. برای شروع، کد زیر را به دقت تجزیه و تحلیل کنید:


public class PolymorphismChallenge {
    static abstract class Simpson {
        void talk() {
            System.out.println("Simpson!");
        }
        protected void prank(String prank) {
            System.out.println(prank);
        }
    }
    static class Bart extends Simpson {
        String prank;
        Bart(String prank) { this.prank = prank; }
        protected void talk() {
            System.out.println("Eat my shorts!");
        }
        protected void prank() {
            super.prank(prank);
            System.out.println("Knock Homer down");
        }
    }
    static class Lisa extends Simpson {
        void talk(String toMe) {
            System.out.println("I love Sax!");
        }
    }
    public static void main(String... doYourBest) {
        new Lisa().talk("Sax :)");
        Simpson simpson = new Bart("D'oh");
        simpson.talk();
        Lisa lisa = new Lisa();
        lisa.talk();
        ((Bart) simpson).prank();
    }
}

نظر شما چیست؟ خروجی نهایی چه خواهد بود؟ برای فهمیدن این موضوع از IDE استفاده نکنید! نکته این است که مهارت های تجزیه و تحلیل کد خود را بهبود ببخشید، بنابراین سعی کنید خروجی را برای خود تعیین کنید.

پاسخ خود را انتخاب کنید و می‌توانید پاسخ صحیح را در زیر بیابید.

A)


       I love Sax!
       D'oh
       Simpson!
       D'oh

B)


       Sax :)
       Eat my shorts! 
       I love Sax!
       D'oh
       Knock Homer down

C)


       Sax :)
       D'oh
       Simpson!
       Knock Homer down

D)


       I love Sax!
       Eat my shorts! 
       Simpson!
       D'oh
       Knock Homer down

حل چالش

برای فراخوانی روش زیر:


new Lisa().talk("Sax :)");

خروجی خواهد بود “I love Sax!” این به این دلیل است که ما یک رشته را به متد ارسال می کنیم و Lisa متد را دارد. .

برای فراخوان بعدی:


Simpson simpson = new Bart("D'oh");
simpson.talk();

خروجی “ شورت من را بخور!” خواهد بود زیرا ما نوع Simpson را با Bart نمونه‌سازی می‌کنیم.

اکنون این مورد را بررسی کنید، که کمی پیچیده‌تر است:

شاید به این مطالب علاقمند باشید