شنبه ۲۹ دي ۱۴۰۳
Tut24 آموزش برنامه نویسی و مجله تخصصی فناوری ورود/عضویت

آموزش جاوا - چند ریختی

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

هر شیء جاوا که می‌تواند بیش از یک آزمون IS-A را پاس کند، به عنوان پلی‌مورفیک در نظر گرفته می‌شود. در جاوا، همه شیءهای جاوا پلی‌مورفیک هستند، زیرا هر شیء برای نوع خودش و برای کلاس Object آزمون IS-A را پاس می‌دهد.

مهم است که بدانید تنها راه ممکن برای دسترسی به یک شیء از طریق یک متغیر مرجع است. یک متغیر مرجع می‌تواند فقط یک نوع باشد. بعد از اعلام، نوع یک متغیر مرجع قابل تغییر نمی‌باشد.

متغیر مرجع می‌تواند به شیء‌های دیگری اشاره کند تا زمانی که به عنوان final اعلام نشده باشد. نوع متغیر مرجع مشخص خواهد کرد که چه متدهایی روی شیء قابل فراخوانی است.

یک متغیر مرجع می‌تواند به هر شیء از نوع اعلام شده خود یا هر زیرنوعی از نوع اعلام شده اشاره کند. یک متغیر مرجع می‌تواند به عنوان یک کلاس یا نوع رابط اعلام شود.

مثال

بیایید به یک مثال نگاه کنیم.

public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}

حالا، کلاس Deer به عنوان یک پلی‌مورفیک در نظر گرفته می‌شود زیرا این کلاس چندتایی ارث بری دارد. موارد زیر برای مثال‌های بالا درست است:

  • یک Deer یک Animal است (A Deer IS-A Animal)
  • یک Deer یک گیاهخوار است (A Deer IS-A Vegetarian)
  • یک Deer یک Deer است (A Deer IS-A Deer)
  • یک Deer یک شیء است (A Deer IS-A Object)

وقتی ویژگی‌های متغیر مرجع را به یک مرجع شیء Deer اعمال می‌کنیم، اعلامیه‌های زیر قانونی هستند:

مثال

Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;

تمام متغیرهای مرجع d، a، v، o به همان شیء Deer در حافظه اشاره می‌کنند.

متدهای مجازی

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

قبلاً درباره override کردن متدها صحبت کرده‌ایم، جایی که یک کلاس فرزند می‌تواند یک متد را در کلاس پدر خود override کند. یک متد override شده در واقع در کلاس پدر مخفی می‌شود و تا زمانی که کلاس فرزند درون متد override از کلمه کلیدی super استفاده کند، فراخوانی نمی‌شود.

مثال

/* File name : Employee.java */
public class Employee {
   private String name;
   private String address;
   private int number;

   public Employee(String name, String address, int number) {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }

   public void mailCheck() {
      System.out.println("Mailing a check to " + this.name + " " + this.address);
   }

   public String toString() {
      return name + " " + address + " " + number;
   }

   public String getName() {
      return name;
   }

   public String getAddress() {
      return address;
   }

   public void setAddress(String newAddress) {
      address = newAddress;
   }

   public int getNumber() {
      return number;
   }
}

حال فرض کنید که کلاس Employee را به شکل زیر گسترش دهیم:

/* File name : Salary.java */
public class Salary extends Employee {
   private double salary; // Annual salary
   
   public Salary(String name, String address, int number, double salary) {
      super(name, address, number);
      setSalary(salary);
   }
   
   public void mailCheck() {
      System.out.println("Within mailCheck of Salary class ");
      System.out.println("Mailing check to " + getName()
      + " with salary " + salary);
   }
   
   public double getSalary() {
      return salary;
   }
   
   public void setSalary(double newSalary) {
      if(newSalary >= 0.0) {
         salary = newSalary;
      }
   }
   
   public double computePay() {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
}

حالا، به دقت برنامه زیر را مطالعه کنید و سعی کنید خروجی آن را تشخیص دهید:

/* File name : VirtualDemo.java */
public class VirtualDemo {

   public static void main(String [] args) {
      Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
      Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
      System.out.println("Call mailCheck using Salary reference --");   
      s.mailCheck();
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
   }
}

این کد نتیجه زیر را تولید می‌کند:

خروجی

Constructing an Employee
Constructing an Employee

Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0

Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.0

در اینجا، دو شیء Salary را نمونه‌سازی می‌کنیم. یکی با استفاده از متغیر مرجع Salary s و دیگری با استفاده از متغیر مرجع Employee e.

هنگام فراخوانی s.mailCheck()، کامپایلر در زمان کامپایل mailCheck() را در کلاس Salary مشاهده می‌کند و JVM در زمان اجرا mailCheck() را در کلاس Salary فراخوانی می‌کند.

اما mailCheck() در مورد e بسیار متفاوت است زیرا e یک مرجع Employee است. هنگامی که کامپایلر e.mailCheck() را مشاهده می‌کند، کامپایلر متد mailCheck() را در کلاس Employee مشاهده می‌کند.

در اینجا، در زمان کامپایل، کامپایلر از mailCheck() در Employee استفاده کرده است تا این عبارت را تأیید کند. اما در زمان اجرا، JVM mailCheck() را در کلاس Salary فراخوانی می‌کند.

این رفتار به عنوان فراخوانی متد مجازی شناخته می‌شود و این متدها به عنوان متدهای مجازی شناخته می‌شوند. یک متد override شده در زمان اجرا فراخوانی می‌شود، بدون توجه به نوع داده‌ای که در زمان کامپایل در کد منبع استفاده شده است.