サーチ…


前書き

多態性は、主なOOP(オブジェクト指向プログラミング)の概念の1つです。多型語は、ギリシャ語の "poly"と "morphs"に由来しています。ポリは「多く」を意味し、モーフは「フォーム」(多くの形式)を意味します。

多形性を実行するには2つの方法があります。 メソッドオーバーロードメソッドオーバーライド

備考

Interfacesは、クラスベースの継承とは別に、Javaで多態性を実現する別の方法です。インタフェースは、プログラムのAPIを形成するメソッドのリストを定義します。クラスは、すべてのメソッドをオーバーライドしてinterface implementする必要があります。

メソッドのオーバーロード

メソッドのオーバーロードは関数のオーバー ロードとも呼ばれ、クラスの数または型のいずれかが異なることを条件に、同じ名前の複数のメソッドを持つクラスの能力です。

コンパイラはメソッドオーバーロードのメソッドシグネチャをチェックします。

メソッドのシグネチャは3つのものから構成されます。

  1. メソッド名
  2. パラメータ数
  3. パラメータの種類

これらの3つがクラス内の任意の2つのメソッドで同じ場合、コンパイラは重複したメソッドエラーをスローします

このタイプの多型は、呼び出される適切なメソッドが引数リストに基づいてコンパイル時にコンパイラによって決定されるため、 静的またはコンパイル時の多態性と呼ばれます

class Polymorph {

    public int add(int a, int b){
        return a + b;
    }
    
    public int add(int a, int b, int c){
        return a + b + c;
    }

    public float add(float a, float b){
        return a + b;
    }

    public static void main(String... args){
        Polymorph poly = new Polymorph();
        int a = 1, b = 2, c = 3;
        float d = 1.5, e = 2.5;

        System.out.println(poly.add(a, b));
        System.out.println(poly.add(a, b, c));
        System.out.println(poly.add(d, e));
    }

}

これにより、次の結果が得られます。

2
6
4.000000

オーバーロードされたメソッドは静的でも非静的でもかまいません。これはメソッドのオーバーロードにも影響しません。

public class Polymorph {

    private static void methodOverloaded()
    {
        //No argument, private static method
    }
 
    private int methodOverloaded(int i)
    {
        //One argument private non-static method
        return i;
    }
 
    static int methodOverloaded(double d)
    {
        //static Method
        return 0;
    }
 
    public void methodOverloaded(int i, double d)
    {
        //Public non-static Method
    }
}

また、メソッドの戻り値の型を変更した場合、メソッドのオーバーロードとして取得できません。

public class Polymorph {  

void methodOverloaded(){
    //No argument and No return type
}

int methodOverloaded(){
    //No argument and int return type 
    return 0;
}

メソッドオーバーライド

メソッドのオーバーライドは、サブタイプがスーパータイプの動作を再定義(オーバーライド)する能力です。

Javaでは、これはスーパークラスで定義されたメソッドをオーバーライドするサブクラスに変換されます。 Javaでは、非プリミティブ変数はすべて実際にはreferencesであり、メモリ内の実際のオブジェクトの位置へのポインタに似ています。 referencesは1つの型しかありません。型は宣言された型です。ただし、宣言された型またはそのサブタイプのいずれかのオブジェクトを指すことができます。

referenceでメソッドが呼び出されると、 指し示されている実際のオブジェクトの対応するメソッドが呼び出されます

class SuperType {
    public void sayHello(){
        System.out.println("Hello from SuperType");
    }

    public void sayBye(){
        System.out.println("Bye from SuperType");
    }
}

class SubType extends SuperType {
    // override the superclass method
    public void sayHello(){
        System.out.println("Hello from SubType");
    }
}

class Test {
    public static void main(String... args){
        SuperType superType = new SuperType();
        superType.sayHello(); // -> Hello from SuperType

        // make the reference point to an object of the subclass
        superType = new SubType();
        // behaviour is governed by the object, not by the reference
        superType.sayHello(); // -> Hello from SubType

        // non-overridden method is simply inherited
        superType.sayBye(); // -> Bye from SuperType
    }
}

覚えておくべきルール

サブクラスのメソッドをオーバーライドするには、オーバーライドするメソッド(サブクラスのメソッド)が必要です。

  • 同名
  • プリミティブの場合は同じ戻り型です(サブクラスはクラスに対して許可されていますが、これは共変リターン型とも呼ばれます)。
  • パラメータの同じタイプと順序
  • スーパークラスのメソッドのthrows節で宣言された例外または宣言された例外のサブクラスである例外だけをスローすることがあります。例外をスローしないことも選択できます。パラメータ型の名前は関係ありません。例えば、void methodX(int i)はvoid methodX(int k)と同じです。
  • finalメソッドやstaticメソッドをオーバーライドすることはできません。私たちが行うことができるのはメソッドボディだけを変更することだけです。

既存のコードに触れずにクラスを追加することで動作を追加する

import java.util.ArrayList;
import java.util.List;

import static java.lang.System.out;

public class PolymorphismDemo {

    public static void main(String[] args) {
        List<FlyingMachine> machines = new ArrayList<FlyingMachine>();
        machines.add(new FlyingMachine());
        machines.add(new Jet());
        machines.add(new Helicopter());
        machines.add(new Jet());

        new MakeThingsFly().letTheMachinesFly(machines);
    }
}

class MakeThingsFly {
    public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
        for (FlyingMachine flyingMachine : flyingMachines) {
            flyingMachine.fly();
        }
    }
}

class FlyingMachine {
    public void fly() {
        out.println("No implementation");
    }
}

class Jet extends FlyingMachine {
    @Override
    public void fly() {
        out.println("Start, taxi, fly");
    }

    public void bombardment() {
        out.println("Fire missile");
    }
}

class Helicopter extends FlyingMachine {
    @Override
    public void fly() {
        out.println("Start vertically, hover, fly");
    }
}

説明

a) MakeThingsFlyクラスは、 FlyingMachine型のすべてで動作します。

b) letTheMachinesFlyメソッドは、 PropellerPlaneように新しいクラスを追加するときにも変更なしで動作します(!)。

public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
        for (FlyingMachine flyingMachine : flyingMachines) {
            flyingMachine.fly();
        }
    }
}

それが多形性の力です。 オープン・クローズド・プリンシプルを実装することができます。

仮想関数

仮想メソッドは静的ではなく、FinalというキーワードがないJavaのメソッドです。デフォルトではすべてのメソッドがJavaの仮想メソッドです。オーバーライドされる関数が非静的で、同じメソッドシグネチャを持つ場合、Javaの子クラスが親クラスのメソッドをオーバーライドできるため、バーチャルメソッドは多態性において重要な役割を果たします。

しかし、仮想ではないいくつかの方法があります。たとえば、メソッドがprivateまたはキーワードfinalで宣言されている場合、メソッドはVirtualではありません。

このStackOverflowポストから仮想メソッドを使用した継承の次の変更例を考えてみましょう。仮想関数はC#とJavaではどのように機能しますか?

public class A{
    public void hello(){
        System.out.println("Hello");
    }
    
    public void boo(){
        System.out.println("Say boo");

    }
}

public class B extends A{
     public void hello(){
        System.out.println("No");
     }
    
    public void boo(){
        System.out.println("Say haha");

    }
}

クラスBを呼び出してhello()とboo()を呼び出すと、BがAと同じメソッドをオーバーライドするため、出力結果として「いいえ」と「いいえ」が得られます。上記の例は、メソッドのオーバーライドを行うには、クラスAのメソッドがデフォルトですべてVirtualであることを理解することが重要です。

さらに、abstractキーワードを使用してVirtualメソッドを実装できます。キーワード "abstract"で宣言されたメソッドにはメソッド定義がありません。つまり、メソッドの本体はまだ実装されていません。上記の例をもう一度考えてみましょう。ただし、boo()メソッドがabstractと宣言されている点が異なります。

public class A{
   public void hello(){
        System.out.println("Hello");
    }
    
    abstract void boo();
}

public class B extends A{
     public void hello(){
        System.out.println("No");
     }
    
    public void boo(){
        System.out.println("Say haha");

    }
}

Bからboo()を呼び出すと、Bは抽象メソッドboo()を継承し、boo()が "Say haha​​"を出力するので、出力は "Say haha​​"になります。

使用されたソースおよびさらなる読み:

C#とJavaで仮想関数はどのように機能しますか?

仮想関数に関するさらに詳しい情報を提供するこの素晴らしい答えを確認してください:

Javaで仮想関数/メソッドを記述できますか?

多型性と異なるタイプのオーバーライド

Java チュートリアルから

多型の辞書の定義は、生物または種が多くの異なる形態または段階を有することができる生物学における原則を指す。この原則は、オブジェクト指向プログラミングやJava言語のような言語にも適用できます。 クラスのサブクラスは独自の動作を定義できますが、親クラスと同じ機能をいくつか共有できます。

この例を見て、異なるタイプの上書きを理解してください。

  1. 基本クラスは実装を提供せず、サブクラスは完全なメソッドをオーバーライドする必要があります - (抽象)
  2. 基本クラスは、デフォルトの実装を提供し、サブクラスは動作を変更することができます
  3. super.methodName() 、最初の文としてsuper.methodName()を呼び出すことによって、基本クラスの実装に拡張を追加します
  4. 基本クラスはアルゴリズムの構造(Templateメソッド)を定義し、サブクラスはアルゴリズムの一部を上書きする

コードスニペット:

import java.util.HashMap;

abstract class Game implements Runnable{

    protected boolean runGame = true;
    protected Player player1 = null;
    protected Player player2 = null;
    protected Player currentPlayer = null;
    
    public Game(){
        player1 = new Player("Player 1");
        player2 = new Player("Player 2");
        currentPlayer = player1;
        initializeGame();
    }

    /* Type 1: Let subclass define own implementation. Base class defines abstract method to force
        sub-classes to define implementation    
    */
    
    protected abstract void initializeGame();
    
    /* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */
    protected void logTimeBetweenMoves(Player player){
        System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime");
    }
    
    /* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling
        super.methodName() in first line of the child class method and specific implementation later */
    protected void logGameStatistics(){
        System.out.println("Base class: logGameStatistics:");
    }
    /* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */
    protected void runGame() throws Exception{
        System.out.println("Base class: Defining the flow for Game:");    
        while (runGame) {
            /*
            1. Set current player
            2. Get Player Move
            */
            validatePlayerMove(currentPlayer);    
            logTimeBetweenMoves(currentPlayer);
            Thread.sleep(500);
            setNextPlayer();
        }
        logGameStatistics();
    }
    /* sub-part of the template method, which define child class behaviour */
    protected abstract void validatePlayerMove(Player p);
    
    protected void setRunGame(boolean status){
        this.runGame = status;
    }
    public void setCurrentPlayer(Player p){
        this.currentPlayer = p;
    }
    public void setNextPlayer(){
        if (currentPlayer == player1) {
            currentPlayer = player2;
        }else{
            currentPlayer = player1;
        }
    }
    public void run(){
        try{
            runGame();
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

class Player{
    String name;
    Player(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}

/* Concrete Game implementation  */
class Chess extends Game{
    public Chess(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized Chess game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate Chess move:" + p.getName());
    }
    protected void logGameStatistics(){
        super.logGameStatistics();
        System.out.println("Child class: Add Chess specific logGameStatistics:");
    }
}
class TicTacToe extends Game{
    public TicTacToe(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized TicTacToe game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate TicTacToe move:" + p.getName());
    }
}

public class Polymorphism{
    public static void main(String args[]){
        try{
        
            Game game = new Chess();
            Thread t1 = new Thread(game);
            t1.start();
            Thread.sleep(1000);
            game.setRunGame(false);
            Thread.sleep(1000);
                        
            game = new TicTacToe();
            Thread t2 = new Thread(game);
            t2.start();
            Thread.sleep(1000);
            game.setRunGame(false);
        
        }catch(Exception err){
            err.printStackTrace();
        }        
    }
}

出力:

Child class: Initialized Chess game
Base class: Defining the flow for Game:
Child class: Validate Chess move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate Chess move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Child class: Add Chess specific logGameStatistics:

Child class: Initialized TicTacToe game
Base class: Defining the flow for Game:
Child class: Validate TicTacToe move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate TicTacToe move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow