/* Further rules concerning abstract classes (and interfaces), static and dynamic types, and type casting. In advanced object oriented programming it is necessary to understand the difference between the static (compile-time) and dynamic (run-time) types of objects. The static type is simply the type that a variable was declared to have. This is the type that the compiler sees and uses to check the consistency of your program. The dynamic type is in fact an additional piece of information that is part of the runtime data structure. It is used at runtime to determine what version of a function to call (dynamic dispatch). Type checking occurs both at compile time, using the statically declared type information, and at run time, using the actual type information available at runtime. As an axiom, *the dynamic type of an object must be a subclass of its static type*. */ // The following abstract class implements a function g and specifies that // all non-abstract subclasses must implement a function f: abstract class AA { public abstract void f(); public void g() { System.out.println("AA.g"); } } // subclass B implements f and an additional function h; inherits g class B extends AA { public void f() {System.out.println("B.f");} public void h() {System.out.println("B.h");} } //subclass C implements f and overrides function g class C extends AA { public void f() {System.out.println("C.f");} public void g() {System.out.println("C.g");} } public class castings { public static void main(String[] args) { B n = new B(); // n has the SAME static and dynamic type of B n.f(); n.g(); n.h(); // prints "B.f", "AA.g" and "B.h" AA m = n; // valid because a B object IS an AA object. AA p; // the static type of p is obviously AA if (args.length>0) p = new B(); // but what's p's dynamic type? else p = new C(); // Obviously the dynamic type of p cannot be known at compile time. AA q = new C(); // the dynamic type of q can still change, so // here again, the static and dynamic types are different q.g(); // this will print "C.g" because of *dynamic dispatch*. the // dynamic type, not the static type is used to determine which g. p.f(); // will print "B.f" or "C.f", also using dynamic dispatch. ///// But now for something that may seem at first non-intuitive: AA r = new B(); // r has static type AA, dynamic type B //r.h(); // Compiler error - because compiler only knows static type! // Not fair? one can get h to be called through TYPE CASTING: ((B)r).h(); // r is type-cast into a B object before h is called. r = new C(); // but now r's dynamic type has changed to C ((B)r).h(); // this line will still compile, but cause a runtime error. /* Type casting is only allowed between types if one inherits from the other (from AA to B and from AA to C). Type casting from the subtype to the supertype is implicit. That is, the line above, AA m = n; required no explicit type casting. Explicit type casting would be needed if the assignment is reversed: n = (B)m; This line will compile because the compiler has been told that m is POSSIBLY a B object. This is possible because B objects are one type of AA objects. But whether this type casting succeeds depends on the actual, dnamic type of the object that m will point to at runtime. */ //B x = (B)(new C()); // compiler error - no cross-casting. }//main }