Java - Đa hình

Tính đa hình là khả năng của một đối tượng có nhiều dạng. Việc sử dụng đa hình phổ biến nhất trong OOP xảy ra khi một tham chiếu lớp cha được sử dụng để tham chiếu đến một đối tượng lớp con.

Bất kỳ đối tượng Java nào có thể vượt qua nhiều hơn một bài kiểm tra IS-A đều được coi là đa hình. Trong Java, tất cả các đối tượng Java là đa hình vì bất kỳ đối tượng nào cũng sẽ vượt qua bài kiểm tra IS-A cho kiểu riêng của chúng và cho đối tượng lớp.

Điều quan trọng cần biết là cách duy nhất có thể để truy cập một đối tượng là thông qua một biến tham chiếu. Một biến tham chiếu có thể chỉ có một kiểu. Sau khi khai báo, không thể thay đổi kiểu của biến tham chiếu.

Biến tham chiếu có thể được gán lại cho các đối tượng khác miễn là nó không được khai báo cuối cùng. Kiểu của biến tham chiếu sẽ xác định các phương thức mà nó có thể gọi trên đối tượng.

Một biến tham chiếu có thể tham chiếu đến bất kỳ đối tượng nào thuộc kiểu khai báo của nó hoặc bất kỳ kiểu con nào của kiểu được khai báo. Một biến tham chiếu có thể được khai báo dưới dạng lớp hoặc kiểu giao diện.

Thí dụ

Chúng ta hãy xem xét một ví dụ.

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

Bây giờ, lớp Deer được coi là đa hình vì nó có đa kế thừa. Sau đây là đúng cho các ví dụ trên -

  • Một con hươu là một con vật
  • A Deer IS-A Vegetarian
  • A Deer IS-A Deer
  • A Deer IS-A Object

Khi chúng tôi áp dụng các dữ kiện về biến tham chiếu cho tham chiếu đối tượng Deer, các khai báo sau là hợp pháp:

Thí dụ

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

Tất cả các biến tham chiếu d, a, v, o tham chiếu đến cùng một đối tượng Deer trong heap.

Phương pháp ảo

Trong phần này, tôi sẽ chỉ cho bạn cách hoạt động của các phương thức bị ghi đè trong Java cho phép bạn tận dụng tính đa hình khi thiết kế các lớp của mình.

Chúng ta đã thảo luận về việc ghi đè phương thức, trong đó một lớp con có thể ghi đè một phương thức trong lớp cha của nó. Một phương thức được ghi đè về cơ bản là ẩn trong lớp cha và không được gọi ra trừ khi lớp con sử dụng từ khóa super trong phương thức ghi đè.

Thí dụ

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

Bây giờ, giả sử chúng ta mở rộng lớp Nhân viên như sau:

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

Bây giờ, bạn nghiên cứu kỹ chương trình sau và cố gắng xác định đầu ra của nó -

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

Điều này sẽ tạo ra kết quả sau:

Đầu ra

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

Ở đây, chúng tôi khởi tạo hai đối tượng Lương. Một sử dụng tham chiếu mức lươngsvà cái khác sử dụng tham chiếu Nhân viên e.

Trong khi gọi s.mailCheck () , trình biên dịch sẽ thấy mailCheck () trong lớp Lương tại thời điểm biên dịch và JVM gọi mailCheck () trong lớp Lương tại thời điểm chạy.

mailCheck () trên e khá khác biệt bởi vì elà một tài liệu tham khảo của Nhân viên. Khi trình biên dịch thấy e.mailCheck () , trình biên dịch sẽ thấy phương thức mailCheck () trong lớp Employee.

Ở đây, tại thời điểm biên dịch, trình biên dịch đã sử dụng mailCheck () trong Employee để xác thực câu lệnh này. Tuy nhiên, tại thời điểm chạy, JVM gọi mailCheck () trong lớp Lương.

Hành vi này được gọi là lệnh gọi phương thức ảo và các phương thức này được gọi là phương thức ảo. Một phương thức ghi đè được gọi trong thời gian chạy, bất kể kiểu dữ liệu nào mà tham chiếu được sử dụng trong mã nguồn tại thời điểm biên dịch.