Java-ポリモーフィズム

ポリモーフィズムは、オブジェクトがさまざまな形をとる能力です。OOPでのポリモーフィズムの最も一般的な使用法は、親クラス参照を使用して子クラスオブジェクトを参照する場合に発生します。

複数のIS-Aテストに合格できるJavaオブジェクトは、ポリモーフィックであると見なされます。Javaでは、すべてのJavaオブジェクトは多形です。これは、どのオブジェクトも、独自のタイプとクラスObjectのIS-Aテストに合格するためです。

オブジェクトにアクセスする唯一の可能な方法は、参照変数を使用することであることを知っておくことが重要です。参照変数は1つのタイプのみにすることができます。宣言されると、参照変数のタイプは変更できません。

参照変数は、finalとして宣言されていない限り、他のオブジェクトに再割り当てできます。参照変数のタイプによって、オブジェクトで呼び出すことができるメソッドが決まります。

参照変数は、宣言された型の任意のオブジェクトまたは宣言された型の任意のサブタイプを参照できます。参照変数は、クラスまたはインターフェイスタイプとして宣言できます。

例を見てみましょう。

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

現在、Deerクラスは多重継承を持っているため、ポリモーフィックであると見なされます。上記の例については、次のことが当てはまります。

  • 鹿IS-動物
  • 鹿IS-菜食主義者
  • 鹿IS-鹿
  • 鹿IS-オブジェクト

参照変数factsをDeerオブジェクト参照に適用する場合、次の宣言は有効です。

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

すべての参照変数d、a、v、oは、ヒープ内の同じDeerオブジェクトを参照します。

仮想メソッド

このセクションでは、Javaでオーバーライドされたメソッドの動作により、クラスを設計するときにポリモーフィズムを利用する方法を示します。

子クラスがその親のメソッドをオーバーライドできるメソッドのオーバーライドについては、すでに説明しました。オーバーライドされたメソッドは基本的に親クラスに隠されており、子クラスがオーバーライドするメソッド内で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

ここでは、2つのSalaryオブジェクトをインスタンス化します。給与参照を使用するものs、およびその他の従業員参照を使用 e

s.mailCheck()を呼び出すと、コンパイラはコンパイル時にSalaryクラスのmailCheck()を認識し、JVMは実行時にSalaryクラスのmailCheck()を呼び出します。

mailCheck()on e まったく違うので e従業員の参照です。コンパイラがe.mailCheck()を認識すると、コンパイラはEmployeeクラスのmailCheck()メソッドを認識します。

ここで、コンパイル時に、コンパイラーはEmployeeでmailCheck()を使用してこのステートメントを検証しました。ただし、実行時に、JVMはSalaryクラスでmailCheck()を呼び出します。

この動作は仮想メソッド呼び出しと呼ばれ、これらのメソッドは仮想メソッドと呼ばれます。オーバーライドされたメソッドは、コンパイル時にソースコードで使用された参照のデータ型に関係なく、実行時に呼び出されます。