Evaluating Method Invocations

 

This explanation will use the following simplified example.

 

class A {

  public void f()    { System.out.println("  A-f"); }

  public void h(A o) { System.out.print("A-h"); }

  public void m(A o) { System.out.print("A-m"); }

}

 

class B extends A {

  public void f()    { System.out.println("  B-f"); }

  public void h(A o) { System.out.print("B-h"); }

  public void m(B o) { System.out.print("B-m"); }

}

 

public class MT2Test1 {

  public static void main(String[] args) { 

    B b1 = new B();

    B b2 = new B();

    A a1 = new A();

    A a2 = b2;

 

    b1.m(a1);

    a2.m(a2);

}

 

Algorithm for evaluating method invocations at compile-time:

·       Determine the declared class of the variable on which the method is being called.

 

B b1 = new B1();

...

b1.m(a1);         // The declared type of b1 is B.

 

·       Start searching in that class for a method with the same name and arguments that match in number and declared type of the objects being passed in.

 

B b1 = new B1();

A a1 = new A1();

...

b1.m(a1);         // Search in B for a method called m that takes

// as an argument a single A (or any superclass

// of A).

 

·       Continue up the inheritance hierarchy looking for a matching method in the same manner as above.  If not found, it is a compile-time error.

 

·       Write down the method descriptor of the method that matches at compile-time.  It will be used in the runtime evaluation.  A method descriptor is the return type and signature of the method.  Remember the signature is the name of the method and the type and order of the arguments.

 

void m(A)         // write this down for use in runtime step

 

Algorithm for evaluating method invocations at runtime:

·       Determine the type of the object to which the variable is bound.  Your search will begin in this class.

 

·       Look for a method with the exact descriptor you found earlier from the compile-time evaluation.

 

·       If found, you’re done.  If not, continue up the inheritance hierarchy.  You’re guaranteed to find an implementation.

 

Walkthrough #1

Compile-time evaluation of b1.m(a1):

 

·       Declared class of b1 is B.  Begin search in this class.

·       Declared class of a1 is A.  Look for method called m in B that takes a single A (or Object) argument.

·       Matching method not found in B so go to A.  Found match!

·       Write down the descriptor of the matching method.  void m(A)

 

Runtime evaluation of b1.m(a1):

 

·       The type of the object bound to b1 is B.  Begin search in this class.

·       Look for method in B with descriptor found in compile-time evaluation.

·       Matching method (void m(A)) not found in B so go to A.  Found match!

·       A’s version of m is executed.

 

Walkthrough #2

Compile-time evaluation of a2.m(a2):

 

·       Declared class of a2 is A.  Begin search in this class.

·       Declared class of a2 is A.  Look for method called m in A that takes a single A (or Object) argument.

·       Found match!

·       Write down the descriptor of the matching method.  void m(A)

 

Runtime evaluation of a2.m(a2):

 

·       The type of the object bound to a2 is B.  Begin search in this class.

·       Look for method in B with descriptor found in compile-time evaluation.

·       Matching method (void m(A)) not found in B so go to A.  Found match!

·       A’s version of m is executed.

 

Further Reading

The source for this material was the Java Language Specification – 15.12 Method Invocation Expressions.  Note that the steps here are vastly simplified.