/* By now you've seen examples of how useful interfaces, abstract classes and inheritance can be. However, to be fully competent in advanced object oriented programming you also need to understand some more subtle points when using these techniques. In particular, you need to understand the difference between static and dynamic dispatch, when to use type casting, as well as be aware of the difference between compile-time and runtime errors. */ class A // superclass, can also be abstract, or be an interface { void f() { System.out.println("A.f called"); } } class B extends A { void f() {System.out.println("B.f called");} void g() {System.out.println("B.g called");} } class C extends A { void h(A x) {System.out.println("C.h(A) called");} void h(B x) {System.out.println("C.h(B) called");} } public class subtleties { public static void main(String[] av) { B x = new B(); // boring, becaue static & dynamic types are same A n = new B(); // static (A) and dynamic types (B) are different if (av.length>0) n = new C(); // what's the dynamic type of n?? n.f(); // dynamic dispatch used here. what does that mean? It uses // the dynamic type of n (B or C) to determine which f to call. //n.g(); // compiler error. why? because A doesn't have g method ((B)n).g(); // will compile, but won't run if n is not dynamic type B //((C)n).h(x); // will this compile? will it run? depends on n again //((C)x).h(n); // this certainly won't compile - can only cast up/down C m = new C(); // at least we both know what kind of object this is! // BUT OVERLOADING DOES NOT USE DYNAMIC DISPATCH: m.h(n); // How does it figure out which version of h to call? // Here it only uses the static type of n (A) to determine // which h to call: this decision is made at compile time. }// }//subtleties // run this program, and modify it to experiment yourself.