Java ポリモーフィズム


ポリモーフィズムとは、同じ動作が複数の異なる表現形式や形態を持つ能力のことです。

ポリモーフィズムとは、同じインターフェースでも、異なるインスタンスを使用することで異なる操作を実行することです。図の通りです:

ポリモーフィズムは、オブジェクトの多様な表現形式の現れです。

現実世界では、例えばF1キーを押すという動作を考えます:

  • 現在Flash画面であれば、ポップアップするのはAS 3のヘルプドキュメントです;
  • Wordであれば、ポップアップするのはWordヘルプです;
  • Windowsであれば、ポップアップするのはWindowsヘルプとサポートです。

同じイベントが異なるオブジェクトで発生すると、異なる結果が生じます。

ポリモーフィズムの利点

  • 1. 型間の結合関係を排除する
  • 2. 置換可能性
  • 3. 拡張可能性
  • 4. インターフェース性
  • 5. 柔軟性
  • 6. 簡素化

ポリモーフィズムが存在するための3つの必要条件

  • 継承
  • オーバーライド
  • 親クラスの参照が子クラスのオブジェクトを指すこと:Parent p = new Child();

class Shape {
    void draw() {}
}
 
class Circle extends Shape {
    void draw() {
        System.out.println("Circle.draw()");
    }
}
 
class Square extends Shape {
    void draw() {
        System.out.println("Square.draw()");
    }
}
 
class Triangle extends Shape {
    void draw() {
        System.out.println("Triangle.draw()");
    }
}

ポリモーフィズム方式でメソッドを呼び出す場合、最初に親クラスにそのメソッドがあるかどうかを確認します。なければコンパイルエラーになります。あれば、子クラスの同名メソッドを呼び出します。

ポリモーフィズムの利点:プログラムの拡張性を高め、すべてのクラスのオブジェクトを汎用的に処理できます。

以下はポリモーフィズムの実例のデモです。詳細はコメントを参照してください:

Test.java ファイルコード:

public class Test { public static void main(String[] args) { show(new Cat()); //Catオブジェクトでshowメソッドを呼び出す show(new Dog()); //Dogオブジェクトでshowメソッドを呼び出す Animal a = new Cat(); //アップキャスト a.eat(); //呼び出されるのはCatのeatです Cat c = (Cat)a; //ダウンキャスト c.work(); //呼び出されるのはCatのworkです } public static void show(Animal a) { a.eat(); //型の判断 if (a instanceof Cat) { //猫がすること Cat c = (Cat)a; c.work(); } else if (a instanceof Dog) { //犬がすること Dog c = (Dog)a; c.work(); } } } abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("魚を食べる"); } public void work() { System.out.println("ネズミを捕まえる"); } } class Dog extends Animal { public void eat() { System.out.println("骨を食べる"); } public void work() { System.out.println("番をする"); } }

上記のプログラムを実行すると、出力結果は次のようになります:

魚を食べる
ネズミを捕まえる
骨を食べる
家を守る
魚を食べる
ネズミを捕まえる

仮想関数

仮想関数はポリモーフィズムのために存在します。

Javaには実際には仮想関数の概念はありません。Javaの普通の関数はC++の仮想関数に相当し、動的束縛はJavaのデフォルトの動作です。Javaで特定の関数に仮想関数の特性を持たせたくない場合は、finalキーワードを付けて非仮想関数にすることができます。

オーバーライド

Javaでは、クラスを設計する際に、オーバーライドされたメソッドの動作がポリモーフィズムにどのように影響するかを説明します。

メソッドのオーバーライドについてはすでに説明しました。つまり、子クラスは親クラスのメソッドをオーバーライドできます。

子クラスのオブジェクトがオーバーライドされたメソッドを呼び出すとき、呼び出されるのは子クラスのメソッドであり、親クラスでオーバーライドされたメソッドではありません。

親クラスでオーバーライドされたメソッドを呼び出すには、キーワードを使用する必要がありますsuper。

Employee.java ファイルコード:

/*ファイル名 : 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("Employee コンストラクタ"); this.name = name; this.address = address; this.number = number; } public void mailCheck() { System.out.println("小切手を送付先:" + 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クラスを継承すると仮定します:

Salary.java ファイルコード:

/*ファイル名 : Salary.java*/ public class Salary extends Employee { private double salary; //年間給与 public Salary(String name, String address, int number, double salary) { super(name, address, number); setSalary(salary); } public void mailCheck() { System.out.println("SalaryクラスのmailCheckメソッド"); System.out.println("小切手を送付先:" + getName() + "、給与は:" + salary); } public double getSalary() { return salary; } public void setSalary(double newSalary) { if(newSalary >= 0.0) { salary = newSalary; } } public double computePay() { System.out.println("給与を計算して、支払い先:" + getName()); return salary/52; } }

次に以下のコードを注意深く読み、その出力結果を考えてみましょう:

VirtualDemo.java ファイルコード:

/*ファイル名 : VirtualDemo.java*/ public class VirtualDemo { public static void main(String [] args) { Salary s = new Salary("従業員 A", "北京", 3, 3600.00); Employee e = new Salary("従業員 B", "上海", 2, 2400.00); System.out.println("Salaryの参照を使用してmailCheckを呼び出す --"); s.mailCheck(); System.out.println("\nEmployeeの参照を使用してmailCheckを呼び出す --"); e.mailCheck(); } }

上記の例をコンパイルして実行した結果は次のとおりです:

Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck -- 
Salary 类的 mailCheck 方法 
邮寄支票给:员工 A ,工资为:3600.0

使用 Employee 的引用调用 mailCheck--
Salary 类的 mailCheck 方法 
邮寄支票给:员工 B ,工资为:2400.0

例の解析

  • この例では、2つのSalaryオブジェクトをインスタンス化しています。1つはSalary参照sを使用し、もう1つはEmployee参照eを使用しています。

  • s.mailCheck()を呼び出すと、コンパイラはコンパイル時にSalaryクラスでmailCheck()を見つけ、実行時にJVMがSalaryクラスのmailCheck()を呼び出します。

  • eはEmployeeの参照ですが、参照eが最終的に実行するのはSalaryクラスのmailCheck()メソッドです。

  • コンパイル時には、コンパイラはEmployeeクラスのmailCheck()メソッドを使用してこの文を検証しますが、実行時にはJava仮想マシン(JVM)がSalaryクラスのmailCheck()メソッドを呼び出します。

上記のプロセス全体は仮想メソッド呼び出しと呼ばれ、そのメソッドは仮想メソッドと呼ばれます。

Javaのすべてのメソッドはこのように動作できます。したがって、オーバーライドされたメソッドは、コンパイル時にソースコード内の参照変数がどのデータ型であっても、実行時に呼び出すことができます。


ポリモーフィズムの実装方法

方法1:オーバーライド:

この内容は前の章で詳しく説明したので、ここでは述べません。詳細は以下を参照してください:Java オーバーライド(Override)とオーバーロード(Overload)。

方法2:インターフェース

  • 1. 身近なインターフェースの代表例はコンセントです。例えば、3極プラグは3口コンセントに差し込めます。これは各国ごとに規定されたインターフェースのルールがあるためです。海外では使えない場合がありますが、それは海外で独自に定義されたインターフェースの種類だからです。

  • 2. Javaのインターフェースは、生活の中のインターフェースに似ており、いくつかのメソッドの特徴の集合ですが、メソッドの実装はありません。詳細はJavaインターフェースこの章の内容を参照してください。

方法3:抽象クラスと抽象メソッド

詳細はJava抽象クラスの章を参照してください。

その他の拡張