LAB 15 - Java Reflection
Goal
Given Note.java, ViolinNote.java HornNote.java- using java reflection try the get the runtime type information (RTTI)
- using java reflection try Introspection of classes
- using java reflection invoke Method objects
- using java reflection make a dynamic instatiation
Java Reflection
Reflection gives your code access to internal information for classes loaded into the JVM and allows you to write code that works with classes selected during execution, not in the source code. You'll want to use the reflection API if you are writing development tools such as debuggers, class browsers, and GUI builders. With the reflection API you can:
- Determine the class of an object.
- Get information about a class's modifiers, fields, methods, constructors, and superclasses.
- Find out what constants and method declarations belong to an interface.
- Create an instance of a class whose name is not known until runtime.
- Get and set the value of an object's field, even if the field name is unknown to your program until runtime.
- Invoke a method on an object, even if the method is not known until runtime.
For every loaded class, the Java Runtime Environment(JRE) maintains an associated Class object The Class object “reflects” the class it represents. We can use the Class object to discover information about a loaded class like name, modifiers (public, abstract, final) , superclasses, implemented interfaces, fields, methods, constructors. Furthemorewe can instantiate classes and invoke their methods via Class object. For accessing the Class object for a loaded class there are three methods:
- To get the Class object for an object mystery: Class c = mystery.getClass();
- Or, using the class name: Class c = Class.forName(“mysteryClass”);
- Can also get the superclass of MysteryClass: Class s = c.getSuperclass();
For introspecting (examining) a class via its Class object
- Getting the class name: Class c = mysteryObject.getClass(); String s = c.getName();
- Discovering the interfaces implemented by a class: Class[] interfaces = c.getInterfaces();
- Discovering the fields of a class: Field[] fields = c.getFields();
- Discovering the methods of a class: Method[] methods = c.getMethods();
RTTI
Consider the following code. What is the output?
Note note;
note = new HornNote();
Class c = note.getClass();
System.out.println("class of note = " + c.getName());
note = new ViolinNote();
c = note.getClass();
System.out.println("now class of note = " + c.getName());
We could also reassign c to reference any super class of ViolinNote. What is the output of the following code:
c = c.getSuperclass();
System.out.println("base class of note = " + c.getName());
c = c.getSuperclass();
System.out.println("base of base class of note = " + c.getName());
Introspection
We can also find out about the methods and fields of a class. Assume that c still references an object of the ViolinNote class. Then the following loop prints out the names of all of the ViolinClass methods:
Method methods[] = c.getMethods(); for(int i = 0; i < methods.length; i++) System.out.println(methods[i].getName());
What is the output? To print the names of the ViolinNote fields as well as their current values in the particular ViolinNote object referenced by note:
Field fields[] = c.getFields();
try {
for(int i = 0; i < fields.length; i++) {
System.out.print(fields[i].getName() + " = ");
System.out.println(fields[i].getInt(note));
}
} catch(Exception e) {
// handle e
}
What is the output?
Invoking Method Objects
We can ask a Method object to invoke the method it represents.(Of course we must provide it with the implicit and explicit arguments.) For example, let's create a generic Note object, then call its play() method using reflection:
note = new Note();
c = note.getClass();
Method meth = c.getMethod("play", null);
meth.invoke(note, null);
What is the output?
Dynamic Instantiation
Consider a universal instrument that can imitate all other types of instruments. This is done with a play() method that expects as its input only the name of the type of note to play:
class UniversalInstrument {
public void play(String noteType) {
try {
Class c = Class.forName(noteType); // find & load a class
Note note = (Note) c.newInstance();
note.play();
} catch (Exception e) {
// handle e here
}
}
}
After creating a universal instrument, our test driver calls the play()method twice. The first time the string "ViolinNote" is the argument. The second time the string "HornNote" is the argument:
UniversalInstrument inst = new UniversalInstrument(); String noteType; noteType = "ViolinNote"; inst.play(noteType); noteType = "HornNote"; inst.play(noteType);
What is the output?