Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Java\'s use of Reflection means that we can access classes and methods on the fl

ID: 667597 • Letter: J

Question

Java's use of Reflection means that we can access classes and methods on the fly, and we can create new
objects and invoke methods from String input. To illustrate these abilities and to cement your understanding of
interpreters, you will create a very small scale "interpreter" for Java. This program will work to load and
execute methods from user input. However, this program will only work on a small subset of Java. We will
only deal with literals that are ints or Strings, and we will only read two types of Java statements: object
creation and method calls. To refer to a standard Java class,we will use the fully specified class name.
Furthermore, we will not really do syntax-checking. If a line has the word "new" in it, we will assume it's an
object creation line. We won't deal with compound method calls, either. Be sure to catch each kind of
exception that can occur and give a detailed error message rather than crashing.
Here is a sample interaction with the Interpreter:
This is a simple interpreter. I'm not a good compiler, so be careful and follow my special
rules:
Each class name should be fully qualified,
I only create objects and call methods.
I only use literals of integers and Strings.
Enter 'Q' to quit.
java.lang.String word = new java.lang.String("Interpreter");
ok. I have a new java.lang.String called word
java.lang.String nextWord = word.substring(0,4);
I made a new object. Result was Inte
word = word.concat(nextWord);
I changed the value of word to my result, InterpreterInte
java.lang.Integer eIndex = word.indexOf("e");
I made a new object. Result was 3
When you finish reading this write-up, start by checking out the code written for you.
Some Definitions
Before starting, make sure you understand the difference between formal and actual parameters (See
http://en.wikipedia.org/wiki/Parameter_%28computer_programming%29). Formal parameters are the holding
place for a variable given in the method header. Actual parameters (also called "arguments") are the values
those methods take on once the method is called. For example, I could have a method header:
public int add(int x, int y)
And call the method like this:
int sum = add(first,second);
The variables x and y are formal parameters and first and second are actual parameters.
Why is this important? Because in this assignment you will be dealing with list of Objects, and those Objects
can be literally anything in all of Java. You have to keep up with what the Object is supposed to represent -
especially between formal and actual parameters.
Using Non-standard Classes
Of course, since Reflection is dynamic, you don't have to be restricted to using standard java objects. There is
an object in the project called MyClass. Here is a sample interaction using that non-standard class:
MyClass obj = new MyClass(3, "Hey!");
ok. I have a new MyClass called obj
String output = obj.toString();
I made a new object. Result was 3 : Hey!
Integer num = obj.getNumber();
I made a new object. Result was 3
output = obj.getMessage();
I changed the value of output to my result, Hey!
obj.print();
3 : Hey!
I performed the operation. My answer is: null
Go Code!
The two objects that you need to fill in are ReflectionUtilities and Interpreter. Look for the "TODO" marking.
Make sure you understand the rest of the code, though, so that you can use it appropriately in your code. You
might want to tackle ReflectionUtilities first, since you will call those methods in Interpreter. There is a JUnit
File called Tester.java, and it will test your ReflectionUtilities code for you somewhat. Please feel free to add
test cases to it.

Here is ReflectionUtilities which has been completed:

import java.lang.reflect.*;

public class ReflectionUtilities {
  
   /* Given a class and an object, tell whether or not the object represents
   * either an int or an Integer, and the class is also either int or Integer.
   * This is really yucky, but the reflection things we need like Class.isInstance(arg)
   * just don't work when the arg is a primitive. Luckily, we're only worrying with ints.
   * This method works - don't change it.
   */
   private static boolean typesMatchInts(Class<?> maybeIntClass, Object maybeIntObj){
       //System.out.println("I'm checking on "+maybeIntObj);
       //System.out.println(maybeIntObj.getClass());
       try{
           return (maybeIntClass == int.class) &&
               (int.class.isAssignableFrom(maybeIntObj.getClass()) ||
                       maybeIntObj.getClass()==Class.forName("java.lang.Integer"));
       }
       catch(ClassNotFoundException e){
           return false;
       }
   }
  
   /*
   * TODO: typesMatch
   * Takes an array of Classes and an array of Objects and tells whether or not
   * the object is an instance of the associated class, and that the two arrays are the
   * same length. For objects, the isInstance method makes this easy. For ints, use the method I
   * provided above.
   */
   public static boolean typesMatch (Class<?>[] formals, Object[] actuals)
   {
       if(formals.length != actuals.length)
   return false;
  
   for(int i = 0; i < formals.length; i++)
   {
   if(actuals[i] == null)
   continue;
  
   if(formals[i].equals(int.class))
   return typesMatchInts(formals[i], actuals[i]);
  
   if(!formals[i].isInstance(actuals[i]))
   return false;
   }
  
   return true;
   }
  
  
   /*
   * TODO: createInstance
   * Given String representing fully qualified name of a class and the
   * actual parameters, returns initialized instance of the corresponding
   * class using matching constructor.
   * You need to use typeMatch to do this correctly. Use the class to
   * get all the Constructors, then check each one to see if the types of the
   * constructor parameters match the actual parameters given.
   */
   public static Object createInstance (String name, Object[] args) throws Exception
   {
       try
   {
   Class<?> cls = Class.forName(name);
   Constructor<?>[] cnstrctrs = cls.getDeclaredConstructors();
  
   for(int i = 0; i < cnstrctrs.length; i++)
   {
   Constructor<?> currConstr = cnstrctrs[i];
  
   Class<?>[] formals = currConstr.getParameterTypes();
  
   if(typesMatch(formals, args))
   {
   return currConstr.newInstance(args);
   }
   }
  
   throw new Exception(name + " has no matched public constructor");
   }
   catch(ClassNotFoundException e)
   {
   throw new Exception(name + " is incorrect class name");
   }
   catch(Exception e)
   {
   throw new Exception(name + " has no matched public constructor");
   }
   }
  
   /*
   * TODO: callMethod
   * Given a target object with a method of the given name that takes
   * the given actual parameters, call the named method on that object
   * and return the result.
   *
   * If the method's return type is void, null is returned.
   *
   * Again, to do this correctly, you should get a list of the object's
   * methods that match the method name you are given. Then check each one to
   * see which has formal parameters to match the actual parameters given. When
   * you find one that matches, invoke it.
   */
   public static Object callMethod (Object target, String name, Object[] args) throws Exception
   {
       try
   {
   Method[] methods = target.getClass().getDeclaredMethods();
  
   for(int i = 0; i < methods.length; i++)
   {
   Method currMeth = methods[i];
  
   if(name.equals(currMeth.getName()))
   {
   Class<?>[] formals = currMeth.getParameterTypes();
  
   if(typesMatch(formals, args))
   {
   return currMeth.invoke(target, args);
   }
   }
   }
   throw new Exception(name + " has no matched public constructor for " + target.getClass().getName());
   }
   catch(Exception e)
   {
   throw new Exception(name + " has no matched public constructor for " + target.getClass().getName());
   }
   }
   }

Now I need the four methods in Interpreter.java

import java.util.HashMap;
import java.util.Scanner;

public class Interpreter {
   // The symbol table is a dictionary of variable identifiers to bindings.
   private HashMap<String, Object> mySymbolTable = new HashMap<String, Object>();
   private Parser myParser;
  
   public Interpreter(){
       mySymbolTable = new HashMap<String,Object>();
       myParser = new Parser();
   }
  
   /*
   * Continually ask the user to enter a line of code.
   */
   public void promptLoop(){
       System.out.println("This is a simple interpreter. I'm not a good compiler, so be careful and follow my special rules: " +
               "Each class name should be fully qualified, "+
               "I only create objects and call methods. " +
               "I only use literals of integers and Strings. "+
               "Enter 'Q' to quit.");
       Scanner scan = new Scanner(System.in);
       String line = scan.nextLine();
       while(!line.equalsIgnoreCase("q")){
           // Find the important words out of the line.
           ParseResults parse = myParser.parse(line);
           // Do the command, and give a String response telling what happened.
           String answer = process(parse);
           System.out.println(answer);
           line = scan.nextLine();
       }
   }
  
   /*
   * This method does the work of carrying out the intended command.
   * The input is a ParseResults object with all the important information
   * from the typed line identified. The output is the String to print telling
   * the result of carrying out the command.
   */
   public String process(ParseResults parse){
       //System.out.println(parse);
       if (parse.isMethodCall){
           return callMethod(parse);
       }
       else return makeObject(parse);
      
   }
  
   /*
   * TODO: convertNameToInstance
   * Given a name (identifier or literal) return the object.
   * If the name is in the symbol table, you can just return the object it is
   * associated with. If it's not in the symbol table, then it is either
   * a String literal or it is an integer literal. Check to see if the
   * first character is quotation marks. If so, create a new String object to
   * return that has the same characters as the name, just without the quotes.
   * If there aren't quotation marks, then it is an integer literal. Use Integer.parseInt
   * to turn the String into an int.
   */
   public Object convertNameToInstance(String name){
       return null;
   }
  
  
   /*TODO: convertNameToInstance.
   * Takes an array of Strings and converts all of them to their associated objects.
   * Simply call the other helper method of the same name on each item in the array.
   */
   public Object[] convertNameToInstance(String[] names){
       return null;
   }
  
  
   /* TODO: makeObject
   * This method does the "process" job for making objects.
   * Don't forget to add the new object to the symbol table.
   * The String that is returned should be a basic message telling what happened.
   */
   public String makeObject(ParseResults parse){      
       return "oops.";
   }
  
   /*
   * TODO: callMethod
   * This method does the "process" job for calling methods.
   * You MUST use the ReflectionUtilities to call the method for
   * this to work, because ints can't be directly transformed into Objects.
   * When you call a method, if it has an output, be sure to
   * either create a new object for that output or change the
   * existing object. This will require either adding a new
   * thing to the symbol table or removing what is currently there
   * and replacing it with something else.
   */
   public String callMethod(ParseResults parse){
       return "oops.";
   }

}

Explanation / Answer

Complete Program:

/*
* Shannon Duvall and <You>
* This object does basic reflection functions
*/
import java.lang.reflect.*;
public class ReflectionUtilities
{
   /*
   * Given a class and an object, tell whether or not the object represents
   * either an int or an Integer, and the class is also either int or Integer.
   * This is really yucky, but the reflection things we need like
   * Class.isInstance(arg)
   * just don't work when the arg is a primitive. Luckily, we're only worrying
   * with ints.
   * This method works - don't change it.
   */
   private static boolean typesMatchInts(Class<?> maybeIntClass, Object maybeIntObj)
   {
       // System.out.println("I'm checking on "+maybeIntObj);
       // System.out.println(maybeIntObj.getClass());
       try
       {
           return (maybeIntClass == int.class)
                   && (int.class.isAssignableFrom(maybeIntObj.getClass()) || maybeIntObj
                           .getClass() == Class.forName("java.lang.Integer"));
       }
       catch(ClassNotFoundException e)
       {
           return false;
       }
   }

   /*
   * TODO: typesMatch
   * Takes an array of Classes and an array of Objects and tells whether or
   * not
   * the object is an instance of the associated class, and that the two
   * arrays are the
   * same length. For objects, the isInstance method makes this easy. For
   * ints, use the method I
   * provided above.
   */
   public static boolean typesMatch(Class<?>[] formals, Object[] actuals)
   {
       if(formals.length != actuals.length)
           return false;
      
       for(int i = 0; i < formals.length; i++)
       {
           if(actuals[i] == null)
                continue;
          
           if(formals[i].equals(int.class))
               return typesMatchInts(formals[i], actuals[i]);
              
           if(!formals[i].isInstance(actuals[i]))
               return false;          
       }
      
       return true;
   }

   /*
   * TODO: createInstance
   * Given String representing fully qualified name of a class and the
   * actual parameters, returns initialized instance of the corresponding
   * class using matching constructor.
   * You need to use typeMatch to do this correctly. Use the class to
   * get all the Constructors, then check each one to see if the types of the
   * constructor parameters match the actual parameters given.
   */
   public static Object createInstance(String name, Object[] args) throws Exception
   {
       try
       {
           Class<?> cls = Class.forName(name);          
           Constructor<?>[] cnstrctrs = cls.getDeclaredConstructors();
          
           for(int i = 0; i < cnstrctrs.length; i++)
           {
               Constructor<?> currConstr = cnstrctrs[i];
              
               Class<?>[] formals = currConstr.getParameterTypes();
              
               if(typesMatch(formals, args))
               {
                   return currConstr.newInstance(args);
               }
           }
          
           throw new Exception(name + " has no matched public constructor");
       }
       catch(ClassNotFoundException e)
       {
           throw new Exception(name + " is incorrect class name");
       }
       catch(Exception e)
       {
           throw new Exception(name + " has no matched public constructor");
       }
   }

   /*
   * TODO: callMethod
   * Given a target object with a method of the given name that takes
   * the given actual parameters, call the named method on that object
   * and return the result.
   *
   * If the method's return type is void, null is returned.
   *
   * Again, to do this correctly, you should get a list of the object's
   * methods that match the method name you are given. Then check each one to
   * see which has formal parameters to match the actual parameters given.
   * When
   * you find one that matches, invoke it.
   */
   public static Object callMethod(Object target, String name, Object[] args) throws Exception
   {
       try
       {
           Method[] methods = target.getClass().getDeclaredMethods();
          
           for(int i = 0; i < methods.length; i++)
           {
               Method currMeth = methods[i];
              
               if(name.equals(currMeth.getName()))
               {
                   Class<?>[] formals = currMeth.getParameterTypes();
                  
                   if(typesMatch(formals, args))
                   {
                       return currMeth.invoke(target, args);
                   }
               }
           }
           throw new Exception(name + " has no matched public constructor for " + target.getClass().getName());
       }
       catch(Exception e)
       {
           throw new Exception(name + " has no matched public constructor for " + target.getClass().getName());
       }
   }
}

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote