public class A { public boolean t() { return true; } public void m() { System.out.println( " A.m() " ); } } public class Aprime extends A { public boolean t() { return false; } public void m() { System.out.println( " A'.m() " ); } } public class BdoublePrime extends B { public void m() { System.out.println( " B''.m() " ); } } public abstract class B { public abstract void m(); public void n( A a ) { System.out.println( " B.n( A ) " ); a.m(); } } public class Bprime extends B { public int nonIdempotent( int a ) { return a; } // from LessSimple.java public void m() { int a = 5; int b = 4; int w = a + b; int x = 5 + 4; int y = x + 10; int z = w + 12; y = z * x; // what happens to z? z = nonIdempotent( y ); System.out.println( " B'.m() " ); } } // demonstrates interprocedural (and also intraprocedural) class analysis public class ClassHierarchies { public static void main(String[] args) { A a = new A(); a.m(); // INTRAPROCEDURAL example: A aPrime = new Aprime(); // although aPrime declared only as type A, aPrime.m(); // we should determine that it's of type A' // and statically call A'.m(), not A.m() B b = null; if ( aPrime.t() ) // and here statically call A'.t(), not A.t() b = new Bprime(); else b = new BdoublePrime(); // INTERPROCEDURAL example: b.n( aPrime ); // b.n() should statically call A'.m() (see B.java) b.m(); // can't statically determine b's class because of branch, // so we should not generate any static function call } }