JAvaシングル・ディスパッチとマルチ・ディスパッチ(マルチ・ディスパッチとシングル・パス・ディスパッチ)


1.          ぶんぱの概念
変数が宣言されたときのタイプを変数の静的タイプ(Static Type)とし、明示的タイプ(Apparent Type)と呼ぶ.変数が参照するオブジェクトの真のタイプは、変数の実際のタイプとも呼ばれます.
オブジェクトのタイプに応じてメソッドを選択することが,ディスパッチである.配分が発生する時期によって,配分を静的配分と動的配分の2つに分けることができる.
静的ディスパッチ(Static Dispatch)はコンパイル時に発生し,ディスパッチは静的タイプ情報に基づいて発生する.メソッドリロード(Overload)は静的割当てである.(いわゆる:コンパイル時マルチステート)
ダイナミックディスパッチ(Dynamic Dispatch)は、実行時に発生し、ダイナミックディスパッチはメソッドを動的に置き換えます.オブジェクト向け言語は,動的割り当てを用いて方法置換による多態性を実現する.(いわゆる:ランタイムマルチステート)
メソッドリロード(静的割り当て)
Javaはメソッドリロードによって静的割り当てをサポートする.次に墨子が馬に乗る物語を考察する.
public class Mozi
{
    public void ride(Horse h)
    {
		System.out.println("Riding a horse");
    }

    public void ride(WhiteHorse wh)
    {
		System.out.println("Riding a white horse");
    }

    public void ride(BlackHorse bh)
    {
		System.out.println("Riding a black horse");
    }

    public static void main(String[] args)
    {
        Horse wh = new WhiteHorse();

        Horse bh = new BlackHorse();

        Mozi mozi = new Mozi();

        mozi.ride(wh);
        mozi.ride(bh);
    }

    /**
     * @directed 
     */
    private Horse lnkHorse;
}

「Riding a horse」を2回印刷しました.墨子は彼が馬に乗っていることに気づいた.
2回のride()メソッドの呼び出しは、異なるパラメータ、すなわちwhおよびbhに伝達される.実際のタイプは異なりますが、静的タイプは同じで、いずれもHorseタイプです.
リロードメソッドの割り当ては静的タイプに基づいて行われる.この割り当てプロセスはコンパイル期間中に完了した.
ダイナミックディスパッチ
Javaはメソッドの置き換え(Overriding)によって動的割り当てをサポートする.
String s1 = "ab";
Object o = s1 +"c";
String s = "abc";
boolean b = o.equals(s);

上記のコードはtrueを返します(ベースマルチステートは多くありません).
2.          割り当てのタイプ
1つのメソッドが属するオブジェクトをメソッドの受信者と呼び,メソッドの受信者とメソッドのパラメータを総称してメソッドの宗量と呼ぶ.
割り当てがどのような宗量に基づいてもよいかによって,オブジェクト向けの言語を単分割言語と多分割言語に分けることができる.ユニット割り当て言語は、1つの宗量のタイプ(実際のタイプ)に基づいてメソッドの選択を行い、マルチ割り当て言語は、複数の宗量のタイプに基づいてメソッドを選択する.
C++とJavaおよびSmaltalkはいずれも単発言語である.マルチディスパッチ言語の例にはCLOSとCecilがある.このような区分によれば、C++とJavaは動的単一割り当て言語である.この2つの言語の動的割り当ては、メソッドの受信者のタイプのみを考慮し、静的多重割り当て言語でもあるからである.この2つの言語は、リロードメソッドの割り当てに対してメソッドの受信者のタイプとメソッドのすべてのパラメータのタイプを考慮するからである.
       動的単一割り当てをサポートする言語では、1つのリクエストがどのアクションを呼び出すかを決定する2つの条件があります.1つはリクエストの名前、2つは受信者の実際のタイプです.単一派閥は、方法の選択プロセスを制限し、この宗量が通常方法の受信者であることを考慮できるのは1つの宗量だけである.JAVA言語では、あるタイプの不明なオブジェクトに操作が作用している場合があります.このオブジェクトの実際のタイプテストは1回しか発生しません.これがダイナミックな単分派の特徴です.
       一言で言えば、JAVA言語は静的な多分派と動的な単分派をサポートしている.
3.          にじゅうぶんぱ
1つの方法は、2つの宗量のタイプに基づいて異なるコードを実行することを決定します.これは、「2つの派閥」または「複数の派閥」です.Javaはダイナミックなマルチキャストをサポートしていません.しかし、設計モードを使用することで、Java言語で動的な二重配分を実現することができます(ps:“偽二重配分”は2回の単一配分から構成されています).
方案一:類型判断
メソッドではinstanceofを使用して実際のタイプを判断します.たとえば、(java.awt.Componentのソースコード):
protected void processEvent(AWTEvent e) {
        if (e instanceof FocusEvent) {
            processFocusEvent((FocusEvent)e);

        } else if (e instanceof MouseEvent) {
            switch(e.getID()) {
              case MouseEvent.MOUSE_PRESSED:
              case MouseEvent.MOUSE_RELEASED:
              case MouseEvent.MOUSE_CLICKED:
              case MouseEvent.MOUSE_ENTERED:
              case MouseEvent.MOUSE_EXITED:
                  processMouseEvent((MouseEvent)e);
                  break;
              case MouseEvent.MOUSE_MOVED:
              case MouseEvent.MOUSE_DRAGGED:
                  processMouseMotionEvent((MouseEvent)e);
                  break;
              case MouseEvent.MOUSE_WHEEL:
                  processMouseWheelEvent((MouseWheelEvent)e);
                  break;
            }

        } else if (e instanceof KeyEvent) {
            processKeyEvent((KeyEvent)e);

        } else if (e instanceof ComponentEvent) {
            processComponentEvent((ComponentEvent)e);
        } else if (e instanceof InputMethodEvent) {
            processInputMethodEvent((InputMethodEvent)e);
        } else if (e instanceof HierarchyEvent) {
            switch (e.getID()) {
              case HierarchyEvent.HIERARCHY_CHANGED:
                  processHierarchyEvent((HierarchyEvent)e);
                  break;
              case HierarchyEvent.ANCESTOR_MOVED:
              case HierarchyEvent.ANCESTOR_RESIZED:
                  processHierarchyBoundsEvent((HierarchyEvent)e);
                  break;
            }
        }
    }

この方法で実現される二重配分は、特に冗長で複雑で誤りやすく、「開閉原則」にも合致しない.
シナリオ2:逆転球
2回の呼び出しで実現されます.例えば、次のハサミ石布のゲームです.
public enum Outcome { WIN, LOSE, DRAW } ///:~


interface Item {
	Outcome compete(Item it);

	Outcome eval(Paper p);

	Outcome eval(Scissors s);

	Outcome eval(Rock r);
}

class Paper implements Item {
	public Outcome compete(Item it) {
		return it.eval(this);
	}

	public Outcome eval(Paper p) {
		return DRAW;
	}

	public Outcome eval(Scissors s) {
		return WIN;
	}

	public Outcome eval(Rock r) {
		return LOSE;
	}

	public String toString() {
		return "Paper";
	}
}

class Scissors implements Item {
	public Outcome compete(Item it) {
		return it.eval(this);
	}

	public Outcome eval(Paper p) {
		return LOSE;
	}

	public Outcome eval(Scissors s) {
		return DRAW;
	}

	public Outcome eval(Rock r) {
		return WIN;
	}

	public String toString() {
		return "Scissors";
	}
}

class Rock implements Item {
	public Outcome compete(Item it) {
		return it.eval(this);
	}
	
	public Outcome eval(Paper p) {
		return WIN;
	}

	public Outcome eval(Scissors s) {
		return LOSE;
	}

	public Outcome eval(Rock r) {
		return DRAW;
	}

	public String toString() {
		return "Rock";
	}
}

public class RoShamBo1 {
	static final int SIZE = 20;
	private static Random rand = new Random(47);

	public static Item newItem() {
		switch (rand.nextInt(3)) {
		default:
		case 0:
			return new Scissors();
		case 1:
			return new Paper();
		case 2:
			return new Rock();
		}
	}

	public static void match(Item a, Item b) {
		System.out.println(a + " vs. " + b + ": " + a.compete(b));
	}

	public static void main(String[] args) {
		for (int i = 0; i < SIZE; i++)
			match(newItem(), newItem());
	}
} 

RoshamBol.match()には2つのitemパラメータがあり,Item.compete()メソッドをクリアして2ウェイ配布を開始し,aのタイプを判定するには,配布メカニズムがaの実際のタイプのcompete()内部で配布の役割を果たす.compete()メソッドはeval()をオフにして別のタイプの2回目の配布を実現し,自身(this)をパラメータとしてeval()を呼び出し,リロードしたeval()メソッドを呼び出すことができ,これにより1回目の配布のタイプ情報を保持することができ,2回目の配布が完了すると,2つのItemオブジェクトの具体的なタイプを知ることができる.
このような実現は「訪問者モード」のエッセンスである.
       このような詳細な説明は以下を参照してください.
      JAvaマルチ配信の実現
『javaとモード』に整理