Java\'s use of Reflection means that we can access classes and methods on the fl
ID: 667524 • 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 the code for ReflectionUtilities: Just fill out only that code
/*
* 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)
{
return false;
}
/*
* 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)
{
return null;
}
/*
* 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)
{
return null;
}
}
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());
}
}
}
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.