Java - polimorfizm

Polimorfizm to zdolność obiektu do przybierania wielu form. Najbardziej powszechne użycie polimorfizmu w OOP występuje, gdy odwołanie do klasy nadrzędnej jest używane w celu odniesienia się do obiektu klasy potomnej.

Każdy obiekt Java, który może przejść więcej niż jeden test IS-A, jest uważany za polimorficzny. W Javie wszystkie obiekty Java są polimorficzne, ponieważ każdy obiekt przejdzie test IS-A dla własnego typu i dla klasy Object.

Ważne jest, aby wiedzieć, że jedynym możliwym sposobem uzyskania dostępu do obiektu jest użycie zmiennej referencyjnej. Zmienna odniesienia może być tylko jednego typu. Po zadeklarowaniu nie można zmienić typu zmiennej referencyjnej.

Zmienną referencyjną można ponownie przypisać do innych obiektów, pod warunkiem, że nie zostanie zadeklarowana jako ostateczna. Typ zmiennej referencyjnej określałby metody, które może ona wywołać na obiekcie.

Zmienna referencyjna może odnosić się do dowolnego obiektu zadeklarowanego typu lub dowolnego podtypu zadeklarowanego typu. Zmienną referencyjną można zadeklarować jako klasę lub typ interfejsu.

Przykład

Spójrzmy na przykład.

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

Teraz klasa Deer jest uważana za polimorficzną, ponieważ ma wielokrotne dziedziczenie. Poniższe są prawdziwe dla powyższych przykładów -

  • Jeleń jest zwierzęciem
  • Jeleń jest wegetarianinem
  • Jeleń to jeleń
  • Jeleń to obiekt

Kiedy stosujemy faktyczne zmienne odniesienia do odniesienia do obiektu Deer, następujące deklaracje są legalne -

Przykład

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

Wszystkie zmienne odniesienia d, a, v, o odnoszą się do tego samego obiektu Deer w stercie.

Metody wirtualne

W tej sekcji pokażę, w jaki sposób zachowanie metod przesłoniętych w Javie pozwala wykorzystać polimorfizm podczas projektowania klas.

Omówiliśmy już przesłanianie metody, w której klasa potomna może przesłonić metodę w jej rodzicu. Przesłonięta metoda jest zasadniczo ukryta w klasie nadrzędnej i nie jest wywoływana, chyba że klasa podrzędna używa słowa kluczowego super w ramach metody przesłaniającej.

Przykład

/* 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;
   }
}

Teraz załóżmy, że rozszerzamy klasę Employee w następujący sposób -

/* 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;
   }
}

Teraz dokładnie przestudiuj poniższy program i spróbuj określić jego wynik -

/* 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();
   }
}

To da następujący wynik -

Wynik

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

Tutaj tworzymy wystąpienie dwóch obiektów Salary. Jeden z odniesieniem do wynagrodzeniasa drugi przy użyciu odwołania pracownika e.

Podczas wywoływania s.mailCheck () kompilator widzi mailCheck () w klasie Salary w czasie kompilacji, a JVM wywołuje funkcję mailCheck () w klasie Salary w czasie wykonywania.

mailCheck () włączony e jest zupełnie inna, ponieważ eto odniesienie do pracownika. Kiedy kompilator widzi e.mailCheck () , kompilator widzi metodę mailCheck () w klasie Employee.

Tutaj, w czasie kompilacji, kompilator użył mailCheck () w Employee, aby sprawdzić poprawność tej instrukcji. Jednak w czasie wykonywania maszyna JVM wywołuje funkcję mailCheck () w klasie Salary.

To zachowanie jest określane jako wywołanie metody wirtualnej, a metody te są określane jako metody wirtualne. Zastąpiona metoda jest wywoływana w czasie wykonywania, bez względu na typ danych, do których odwołuje się odwołanie, który został użyty w kodzie źródłowym w czasie kompilacji.