Title: Reflection in Java
1sysc2101 Object-Oriented Programming and Design
Laboratory
2Reflection
- In computer science, Reflection is the domain of
programs that describe and manipulate
"themselves", or other related programs. - In pure object-oriented languages such as
Smalltalk, this is achieved by considering
Classes as regular objects - as such, a Class is itself an instance of the
Class of Classes, also called a Metaclass - this allows for an elegant way
- to inspect properties of a class
- to create new classes at runtime,
- or to modify their behavior dynamically
- powerful but dangerous!
3Reflection in Java
- For each class involved in a Java program, the
Java Runtime Environment (JRE) maintains an
immutable Class object that contains information
about the class. - A Class object represents, or reflects, the
class. - With the reflection API, you can invoke methods
on a Class object which return Constructors,
Methods, and Fields objects.You can use these
objects to get information about the
corresponding constructors, methods, and fields
defined in the class.
4Uses
- To write a class browser you need a way to get
information about classes at runtime. - You can reverse engineer bytecode into source
code - You can develop tools that make use of method
naming conventions to discover properties - JavaBeans
- JUnit
- You can enhance the behavior of a program during
runtime by loading new classes
5Reflection in Java
- Using the classes of the Java reflection API
- Classes in package java.lang
- Class
- Object
- Package
- ClassLoader
- Classes in package java.lang.reflect
- Array
- Method
- Modifier
- Constructor
- Field
6Java reflection mechanisms
- Java reflection mechanisms
- Obtaining information
- Acting on an object
- Working with arrays
7Obtaining information
- Information about a class or the class of an
object, when they are known only at runtime - Name,
- Modifiers,
- Superclass,
- Implemented interfaces,
- Is a Class object representing a class or an
interface, - Fields,
- Constructors,
- Methods.
8Information about a class
- Getting the class of an (mystery) object
- Class c mystery.getClass() //recall instanceof
- Getting the super class of a class
- TextField t new TextField()
- Class c t.getClass()
- Class s c.getSuperclass()
- Getting a class from its name at compile time
- Class c java.awt.Button.class
- Getting a class from its name at runtime
- Class c Class.forName(strg)
- Getting the class name of an object
- String s o.getClass().getName()
9Example 1
- Getting the modifiers of a class
- void printModifiers(String name) throws
ClassNotFoundException - out.println(Class name is)
- Class c Class.forName(name)
- int m c.getModifiers()
- if (Modifier.isPublic(m))
- out.println("public")
- else
- out.println("not public")
- if (Modifier.isAbstract(m))
- out.println("abstract")
- else
- out.println("not abstract")
- if (Modifier.isFinal(m))
- out.println("final")
- else
- out.println("not final")
- Example of execution
- printModifiers(java.lang.System)
- printModifiers(java.io.InputStream)
- Result
- Class java.lang.System is
- public
- not abstract
- final
- Class java.io.InputStream is
- public
- abstract
- not final
10Example 2
- Finding ancestors
- void printSuperclass(String name) throws
ClassNotFoundException - Class subClass, superClass
- subClass Class.forName(name)
- superClass subClass.getSuperclass()
- out.println(The ancestors of name are)
- while (superClass ! null)
- out.println(superClass.getName())
- subClass superClass
- superClass subClass.getSuperclass()
-
Example of execution printAncestors(java.io.Buf
feredInputStream)
Result The ancestors of java.io.BufferedInputStre
am are java.io.FilterInputStream java.io.InputStre
am java.lang.Object
11Example 3
- Finding implemented interfaces
- void printInterfaceNames(String name)
- Class c Class.forName(name)
- Class theInterfaces c.getInterfaces()
- out.println(The interfaces implemented by
name are) - for (int i0 ilttheInterfaces.length i)
- String interfaceName theInterfacesi.getName()
- System.out.println(interfaceName)
-
Example of execution printInterfaceNames(java.ut
il.Vector)
- Result
- The interfaces implemented by java.util.Vector
are - java.util.List
- java.util.RandomAccess
- java.lang.Cloneable
- java.io.Serializable
12Example 4
- Is a Class instance a class or an interface?
- void verifyClassInstance(String name)
- Class c Class.forName(name)
- if (c.isInterface())
- System.out.println(name " is an interface.")
- else
- System.out.println(name " is a class.")
Example of execution verifyClassInstance(java.ut
il.Vector) verifyClassInstance(java.util.Random
Access) Result java.util.Vector is a
class java.util.RandomAccess is an interface
13Example 5
- Identifying class fields
- void printFieldNames(String name)
- Class c Class.forName(name), typeClass
- String fieldType
- Field publicFields c.getFields()
- for (int i 0 i lt publicFields.length i)
- typeClass publicFieldsi.getType()
- fieldType typeClass.getName()
- out.println("Name "publicFieldsi.getName()",T
ype"fieldType) -
- Result
- Name ttype, type int
- Name TT_EOF, type int
- Name TT_EOL, type int
- Name TT_NUMBER, type int
- Name TT_WORD, type int
- Name sval, type java.lang.String
- Name nval, type double
Example of execution printFieldsName(java.io.St
reamTokenizer)
14Example 6
- Showing constructors information
- void showConstructors(String name)
- Class c Class.forName(name)
- Constructor cs c.getConstructors()
- for (int i0 iltcs.length i)
- System.out.print("( ")
- Class pT csi.getParameterTypes()
- for (int k 0 k lt pT.length k )
- System.out.print(pTk.getName() " ")
- System.out.println(")")
-
Example of execution showConstructors(java.util.
Vector)
Result ( ) ( int int ) ( java.util.Collection
) ( int )
15Example 7
- Showing methods information
- void showMethods(String name)
- Class c Class.forName(name)
- Method Ms c.getMethods()
- for (int i 0 i lt Ms.length i)
- out.println("Name " Msi.getName())
- out.println(\tReturn Type "Msi.getReturnType(
).getName()) - Class paramT Msi.getParameterTypes()
- out.print(\tParameter Types ")
- for (int k 0 k lt paramT.length k )
- out.print(" " paramTk.getName())
- out.println()
-
-
16Example 7 (cont.)
- Example of execution
- showMethods(java.util.Vector)
- Result
- Name removeElement
- Return type boolean
- Parameter types java.lang.Object
- Name removeElementAt
- Return type void
- Parameter types int
- Name subList
- Return type java.util.List
- Parameter type int int
- Name iterator
- Return type java.util.Iterator
- Parameter types
17Acting on an object
- Acting on an object when its type is only known
at runtime - Creating an object,
- Getting field values,
- Setting field values,
- Invoking methods.
18Creating an instance with a no-argument
constructor
- Object createObjectDefault(String className)
- Object obj null
- try
- Class classDef Class.forName(className)
- obj classDef.newInstance()
- catch (Exception e)
- // throws ClassNotFoundException,
InstantiationException, IllegalAccessException - System.out.println(e)
-
- return obj
-
- Example of execution
- Rectangle r (Rectangle) createObject("java.awt.R
ectangle") - System.out.println(r)
- Result
- java.awt.Rectanglex0,y0,width0,height0
19Creating an instance with a constructor that have
given argument types
- Object createObject( Constructor constr, Object
arguments) - System.out.println ("Constructor "
constr.toString()) - Object object null
- try
- object constr.newInstance(arguments)
- System.out.println ("Object "
object.toString()) - catch (Exception e)
-
-
- return object
-
20- Rectangle r
- Class rectDef
- Class intArgsClass new Class int.class,
int.class - Integer height new Integer(12)
- Integer width new Integer(34)
- Object intArgs new Object height, width
- Constructor Constr
- try
- rectDef Class.forName("java.awt.Rectangle")
- Constr rectDef.getConstructor(intArgsClass)
- r (Rectangle) createObject(Constr, intArgs)
- catch (Exception e)
-
-
21Getting field values
- void printHeight(Rectangle r)
- Field hF
- Object hV
- Class c r.getClass()
- try
- hF c.getField("height")
- hV hF.get(r)
- System.out.println("Height " hV.toString())
- catch (Exception e)
-
-
22Setting field value
- void modifyWidth(Rectangle r, Integer val )
- Field wF
- Class c r.getClass()
- try
- wF c.getField("width")
- wF.set(r, val)
- catch (Exception e)
-
-
23Invoking methods
- String append(String first, String second)
- String result null
- Class c String.class
- Class paramT new ClassString.class
- Method concatM
- Object args new Object second
- try
- concatM c.getMethod("concat", paramT)
- result (String) concatM.invoke(first, args)
- catch (Exception e)
-
-
- return result
24Working with Arrays
- The java.lang.reflect.Array class provides
methods that allow you to dynamically create and
access arrays. - Working with arrays
- Identifying arrays,
- Retrieving Component Types,
- Creating an array,
- Getting and setting elements.
25Identifying arrays among attributes
- void printArrayNames(String name)
- Class targetClass Class.forName(name),
typeClass - Field publicFields targetClass.getFields()
- for (int i0 i lt publicFields.length i)
- typeClass publicFieldsi.getType()
- if (typeClass.isArray())
- System.out.println( "Name publicFieldsi.get
Name() ", Type typeClass.getName()) -
26Retrieving Component Types
- void printComponentType(Object array)
- Class arrayClass array.getClass(),
componentClass - if (!arrayClass.isArray())
- return
- componentClass arrayClass.getComponentType()
- System.out.println( "Array "
arrayClass.getName() ", Component "
componentClass.getName()) -
- Example of execution
- Object objs new Object new Integer(12),
new Integer(12) - printComponentType(objs)
- int arr new int23
- printComponentType(arr)
- Result
- Array Ljava.lang.Object, Component
java.lang.Object - Array I, Component I
27Creating an array
- Object doubleArray(Object source)
- int sourceLength Array.getLength(source)
- Class arrayClass source.getClass()
- Class componentClass arrayClass.getComponentType
() - Object result Array.newInstance(componentClass,
sourceLength 2) - System.arraycopy(source, 0, result, 0,
sourceLength) - return result
28Getting and setting element values
- void copyArray(Object source, Object dest)
- for (int i0 i lt Array.getLength(source) i)
- Array.set(dest, i, Array.get(source, i))
- System.out.println(Array.get(dest, i))
-
29Working with arrays (example)
- Problem
- Suppose you have an array of some type that is
full and you want to grow it. - And suppose your are sick of writing the
grow-and-copy code by hand. - You can write a generic method to grow an array.
- Solution using reflection mechanisms
- Object arrayGrow(Object a)
- if (!a.getClass().isArray())
- return null
- Class compType a.getClass().getComponentType()
- int length Array.getLength(a)
- int newLength length 1
- Object newArray Array.newInstance(compType,
newLength) - System.arraycopy(a, 0, newArray, 0, length)
- return newArray
30ClassLoader
- A class loader is an object that is responsible
for loading classes - Inherits from abstract class java.lang.ClassLoader
- Given the name of a class, a class loader should
attempt to locate or generate data that
constitutes a definition for the class. - A typical strategy is to transform the name into
a file name and then read a "class file" of that
name from a file system. - Applications implement subclasses of ClassLoader
in order to extend the manner in which the Java
virtual machine dynamically loads classes. - The ClassLoader class uses a delegation model to
search for classes and resources. - Each instance of ClassLoader has an associated
parent class loader. - When called upon to find a class or resource, a
ClassLoader instance will delegate the search for
the class or resource to its parent class loader
before attempting to find the class or resource
itself. - The virtual machine's built-in class loader,
called the bootstrap class loader, does not
itself have a parent but may serve as the parent
of a ClassLoader instance.
31Example
- Problem
- Assume you have to build a tool that analyzes
java source code - e.g., youre building a tool that
reverse-engineers the class diagram - The classes you need the information of are not
the ones of your program, but the ones of another
program (the program of which you want to build
the class diagram) - You cannot use the Class instances from the
ClassLoader in your program - You need a new ClassLoader that can look for
class files in different directories
32Example (cont.)
- public class MyClassLoader extends ClassLoader
- private String thePath
- // the directory where the program to analyze is
located (.class files) - public MyClassLoader()
-
- public void setPath(String p)
- thePath p
- if ( (!thePath.endsWith("/"))
(!thePath.equals("")) ) - thePath "/"
-
- public Class findClass(String className) throws
ClassNotFoundException -
-
- protected byte loadClassBytes(String className)
throws IOException -
33Example (cont.)
- public Class findClass(String className) throws
ClassNotFoundException - byte classBytes null
- Class cl
- try
- classBytes loadClassBytes(className)
- // Reads a class definition from a .class file
- catch (IOException e)
- throw new ClassNotFoundException(className)
-
- try
- cl defineClass(className, classBytes, 0,
classBytes.length) - // Method from class ClassLoader Converts an
array of bytes into - // an instance of class Class
- catch (NoClassDefFoundError r)
- throw new ClassNotFoundException(className)
-
- if (clnull)
- throw new ClassNotFoundException(className)
- return cl
34Example (cont.)
- protected byte loadClassBytes(String className)
throws IOException - String cName
- FileInputStream in null
- ByteArrayOutputStream buffer null
- byte b
- int ch
- cName thePath className.replace('.', '/')
".class" - try
- in new FileInputStream(cName)
- buffer new ByteArrayOutputStream()
- while ((chin.read())! -1)
- b (byte)ch
- buffer.write(b)
-
- in.close()
- return buffer.toByteArray()
- finally
- if (in ! null)
- in.close()
35Two Case Studies
- Writing a correct equals()
- Inside the design of JUnit
36A Correct equals()
- Recall that according to the Java specs the
equals() method should be an equivalence relation
and therefore obey the following specifications - reflexive x.equals(x) true
- symmetric x.equals(y) y.equals(x)
- transitive
- if ((x.equals(y)) (y.equals(z)))
- then (x.equals(z) true)
37A Correct equals()?
- Below is an equals() method written for class
Point - public class Point
- private int x, y
- public boolean equals(Object other)
- if (other instanceof Point)
- otherP (Point) other
- return this.x otherP.x this.y
otherP.y -
- else return false
-
- Is this method following the specs?
38A Correct equals()!
- public boolean equals(Object other)
- if (this.getClass() other.getClass())
- otherP (Point) other
- return this.x otherP.x this.y
otherP.y -
- else return false
-
- Note the use of instead of equals() to
compare the classes - the class variable is a singleton, we can just
compare the references
39Use of Reflection in JUnit
- Recall that each test case must be implemented as
a public method with a name that starts with
testXXX - Such a convention makes life easy for the
developer - But the JUnit framework needs to manipulate each
test case as a separate object, in order to
invoke setUp(), run() and tearDown() for each. - JUnit creates instances of class TestCase and
invokes such methods on them - it does so using reflection!
- In fact, there JUnit uses many design patterns
that are interesting to study by their own right - http//junit.sourceforge.net/doc/cookstour/cooksto
ur.htm