Capire il tipo dinamico

Si consideri il seguente programma in Java
public class B {
	public void foo(B obj) {
		System.out.print("B1 ");
	}
	public void foo(C obj) {
		System.out.print("B2 ");
	}
}
public class C extends B {
	public void foo(B obj) {
		System.out.print("C1 ");
	}
	public void foo(C obj) {
		System.out.print("C2 ");
	}
	public static void main(String[] args) {
		B c = new C();
		B b = new B();
		b.foo(c);
		c.foo(b);
		c.foo(c);
	}
}

Il tipo statico dell'istanza c è B, mentre il suo tipo dinamico è C.
Il tipo statico dell'istanza b è B, e il suo tipo dinamico è B.
Quando chiamo il metodo di un'istanza, vado a cercare il metodo della classe dinamica di essa, mentre se passo un'istanza come parametro di un metodo vado a prendere il suo tipo statico.
Per questo motivo, quando eseguo b.foo(c);, visualizzerò come output B1.
Allo stesso modo, quando eseguo c.foo(b);, visualizzerò come output C1;
quando eseguo invece c.foo(c); visualizzerò come output sempre C1. Ricordiamo inoltre che il tipo statico è il tipo dell'oggetto(quello a sinistra dell'assegnamento), mentre il tipo dinamico è quello dell'istanza dopo la parola chiave new (ovvero a destra dell'assegnamento).