Java Programming Language Basics: Reflection Basics and Class Class
Java Programming Language Basics
Topics: Reflection Basics and Class Class
1. Reflection Basics:
The standard J2SE platform libraries include a reflection API. This API allows classes to reflect on themselves, to see their inner selves. Typically not used by developers, but by tool developers such as those creating an IDE like NetBeans, the reflection API lets you discover the names of classes, methods, and fields. Just finding out the names isn't all you can do though. You can also invoke those methods and access arrays without using square brackets.
The heart of the reflection API is the Class class. This class allows you to find out the name of a class, its access modifiers, fields, methods, and so forth. For any instance of a class, you can get its Class class by calling the getClass method:
Class c = anInstance.getClass();
If you don't happen to have an instance of a class (and don't want to create one), just attach .class to the end of the class name and you have the Class instance for that class.
Class c = MyClass.class;
The same even works for primitives:
Class c = int.class;
This last one might seem odd, but it allows you to specify argument types when calling methods (via Reflection) that accept primitive arguments.
One thing typically done is creating a Class by passing its string name to the forName method of Class.
Class c = Class.forName("java.awt.Button");
This is very common in JDBC technology. This is done so is that so that at compile time you don't have to have the class within the quoted string available. So, if you change JDBC drivers, you won't have to recompile your program if the driver's class name was the quoted string.
Note: When naming the variable for the Class instance, don't name it class. As this is a reserved word, the compiler will think you are trying to define a new class in an inappropriate spot.
Once you have a Class class, that's where the fun begins. You can find out the name of the class with its getName method.
Class c = javax.swing.JButton.class
System.out.println("Name: " + c.getName());
Or, you can find out its superclass with getSuperclass:
System.out.println("Super: " + c.getSuperclass().getName());
[Yields javax.swing.AbstractButton in the case of the JButton.]
Moving from classes to methods takes us to the Method class, found in the java.lang.reflect package. With the Method class, you can discover all methods of a class (with getMethods) and even invoke them (with invoke).
The following program demonstrates getting the methods of a class, where the class name comes from the command line.
import java.lang.reflect.*;
public class ListMethods {
public static void main(String args[]) {
if (args.length == 0) {
System.err.println(
"Please include fully qualified class name on command line");
return;
}
for (int i=0, n=args.length; i<n; i++) {
listMethods(args[i]);
}
}
private static void listMethods(String name) {
try {
Class c = Class.forName(name);
System.out.println("----" + c.getName() + "----");
Method methods[] = c.getMethods();
for (int i=0, n=methods.length; i<n; i++) {
System.out.println(methods[i].getName());
System.out.println("\t" + methods[i]);
}
} catch (ClassNotFoundException e) {
System.out.println("Bad classname: " + name);
}
}
}
For each name passed on the command line, you'll discover the methods available to a class. Run on itself, you get the following output:
----ListMethods----
main
public static void ListMethods.main(java.lang.String[])
hashCode
public native int java.lang.Object.hashCode()
getClass
public final native java.lang.Class java.lang.Object.getClass()
wait
public final void java.lang.Object.wait(long,int)
throws java.lang.InterruptedException
wait
public final void java.lang.Object.wait()
throws java.lang.InterruptedException
wait
public final native void java.lang.Object.wait(long)
throws java.lang.InterruptedException
equals
public boolean java.lang.Object.equals(java.lang.Object)
notify
public final native void java.lang.Object.notify()
notifyAll
public final native void java.lang.Object.notifyAll()
toString
public java.lang.String java.lang.Object.toString()
Notice that the output includes all the methods available through its superclass, too. To limit the output to only those methods declared in the class itself, change the getMethods call to getDeclaredMethods and you'll get the following output instead:
----ListMethods----
main
public static void ListMethods.main(java.lang.String[])
listMethods
private static void ListMethods.listMethods(java.lang.String)
While the getMethods method of Class allows you to get all the methods of a class, more typically, you want a specific method of a class. For that, there is the getMethod(String name, Class types[]) method. By passing in the name of the method you want, and an array of the Class types for the arguments, you can get a specific method. Once you have that Method, you can invoke it with the invoke(Object instance, Object args[]) method. For static methods, the instance argument can be null.
The tricky part of invoking of methods through reflection is the Class[] and Object[] arguments. To find a method, you have to provide an array of the Class types for the arguments. That means if you wanted to locate the following method of String:
regionMatches(boolean ignoreCase,
int toffset,
String other,
int ooffset,
int len)
The Class[] declaration would look something like the following:
Class types[] = {boolean.class,
int.class,
String.class,
int.class,
int.class};
The values for the arguments don't matter when finding a method, only the class types. At invoke time, you pass in the actual argument values. For primitive types, you must box them up as objects (like using Integer for int).
The following demonstrates invoking a method through reflection, and inserting a String in the middle of a StringBuffer:
public StringBuffer insert(int offset, String str)
While regionMatches would work, this example will be a little less complex.
import java.lang.reflect.*;
public class Combine {
public static void main(String args[]) {
try {
StringBuffer buffer = new StringBuffer("Held");
Class c = buffer.getClass();
Class types[] = {int.class, String.class};
Method method = c.getMethod("insert", types);
Object theArgs[] = {new Integer(2), "llo, Wor"};
System.out.println("Before: " + buffer);
method.invoke(buffer, theArgs);
System.out.println("After: " + buffer);
} catch (Exception e) {
System.err.println("Unable to invoke method: " + e);
}
}
}
2. Class Class:
Making Sense of the Java Class
Class Object
Every class, including those from the Java API and any you create, has class Object as a superclass because Object is the root of the class hierarchy.
Although every class extends class Object, you don't need to write:
Object obj = new Dog("Bandit", "Mixed Breed");
Instead, class Dog automatically extends Object, and inherits all of Object's methods. If a class returns a type Object, then you'll need to cast it to the appropriate object:
Dog dg = (Dog)obj;
Class Object defines the basic state and behavior that all objects must have, such as the ability to compare an object to another object, to convert to a string, to wait on a condition variable, to notify other objects that a condition variable has changed, and to return the class of the object. In other words, the real meat of this class is in the methods all your classes and Java classes inherit. In fact, all objects, including arrays, implement the methods of this class.
When you create a class, you automatically have the Object class methods available to your object. Here are a few:
public boolean equals(Object obj) -- Indicates whether some other object is "equal to" this one.
public String toString() -- Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read.
public final void notify() -- Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.
public final void wait(long timeout) throws InterruptedException -- Causes current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
protected void finalize() throws Throwable -- Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup.
protected Object clone() throws CloneNotSupportedException -- Creates and returns a copy of this object.
There are more useful methods available to any class you create or use because all classes inherit from the Object class. Take some time to read through the documentation about this class so you know what is readily and automatically available to your classes.
Source: Java Technology Fundamentals Newsletter.
# Cheat me in price, but not in the goods i purchase - Spanish Proverb
# Childhood and genius have the same master-organ in common: inquisitiveness - E. G. Bulwer Lytton
Bookmarks