Polymorphism and Dynamic Dispatch

Objectives

This lab introduces you to the concepts of polymorphism in Java. You are going to start with the inheritance hierarchy from a previous lab (with minor changes).

  1. Study code to gain a deeper understanding of Polymorphism.

  2. Construct a mechanism for Dynamic Dispatch by working with reflection.

Getting Started

  1. Create a project called L8 and download DynamicDispatch-starter archive:

Your directory structure will look like this:

L8/
└── src
    ├── A.java
    ├── B.java
    ├── C.java
    ├── DynamicDispatch.java
    └── TestCode.java

Part One

Go to the DynamicDispatch class.

You will find a lot of working support code, but the most interesting method is left to you: callMethodFromClassElseFromSuper.

Go back to the testCode comment out the call to polymorphismExercise()

You saw the getClass() method in the previous code, let’s take a closer look at the method.

Exercise one:
  • Go to the main method in the TestCode class. Comment out the call to polymorphismExercise method and comment in the call to the dynamicDispatchExercise method

  • Find the dynamicDispatchExercise method in the TestCode class and complete question 1.

Background

Knowing what the getClass() method shows us, we will implement code to dynamically dispatch to a given method based on the runtime type of an object. Java always performs dynamic dispatch (also called late binding, or dynamic binding, etc) when invoking a method on an object. It looks up the runtime type of the actual object sitting on the JVM heap and then, if the method has been overridden one or more times in the inheritance hierarchy, it invokes the most specific method definition.

We want to model this process of dynamic dispatch by implementing a recursive search for the desired method through the inheritance hierarchy.

Use the following strings to print messages and errors while implementing the callMethodFromClassElseFromSuper method.

String message = String.format("Failed to find a method for %s.%s%s", o.getClass().getName(), method_name, arrayToStringWithParens(arguments));

String.format("Trying to find method [%s] on class [%s]..", method_name, c.getName())

String.format("Found in class %s! Invoking with args %s to produce a %s.\n", c.getName(), Arrays.toString(arguments), m.getReturnType().getName())

String message = String.format("Method %s.%s%s threw:", c.getName(), method_name, arrayToStringWithParens(arguments));
Recipe

Use the following steps to implement the callMethodFromClassElseFromSuper method:

  1. Since we’ll be using recursion, we need to have a base case. We’ll be recursing up the inheritance hierarchy with Class.getSuperClass(). If you do this iteratively we eventually end up with the class Object, which is the parent of all classes in Java. If you call getSuperClass() on Object, it returns null.

    • In your method, first check to see if the Class argument is null. If it is null we know our search failed to find any method to invoke.

      • Use an appropriate message from above to return a useful error message.

      • Throw a new NoSuchMethodException, giving your error message to the constructor.

  2. Next, call the static method log with an appropriate message from above to log a message indicating that you are now trying to find the method on the current class.

  3. Get the declared methods of the class (using getDeclaredMethods()) and loop over them using a for-each loop.

  4. For each Method object, check if its name (returned by getName()) is the name of the method you are looking for.

  5. If it is, further check that its parameter types (returned by getParameterTypes()) are compatible with the arguments you are given. Do this with the provided static method parameterTypesMatchArguments.

  6. If the name matches and the argument types are compatible, we’ve found our method!

    • Use the log method on an appropriate message from above to state you found a suitable method.

  7. Try to return the result of invoking the method on the Object o with the provided arguments.

    • Do this by returning the result of m.invoke(Object o, Object…​ arguments) with m being the current method.

    • Catch any IllegalAccessExceptions and throw a new RuntimeException, giving the constructor the IllegalAccessException you caught.

    • Catch any InvocationTargetExceptions and log the error use an error message from above. Then throw a new RuntimeException, giving it your message and the InvocationTargetException’s cause (use the getCause() method).

  8. If you looped through all the declared methods on the given class, and none of them matched the desired method, then it’s time to continue the search in the parent class, to see if they have a definition for it. Return the result of recursively calling callMethodFromClassElseFromSuper with the super class of the class argument, and the same parameters otherwise.

Exercise two

Once you have finished implementing and testing the method go to the TestCode class and complete question 2.

Part Two

Let’s move on to the polymorphismExercise in the TestCode class

  • Run the main method and follow the instructions.

  • You will be uncommenting and running the code while working through various questions.

Feel free to use google, talk with other students and ask your TAs questions.

Submission

Please show your the TestCode class to your TA and talk about your code and the questions you’ve answered to receive credit