Beruflich Dokumente
Kultur Dokumente
class Reflect {
public static Constructor [] constructors;
public static Method [] methods;
public static Field [] fields;
public static Class thisClass;
static {
try {
thisClass = Class.forName("Reflect");
// dissect and see this class itself!
}
catch(ClassNotFoundException cnfe) {
System.out.println("class doesn't exist " + cnfe);
System.exit(-1);
}
constructors = thisClass.getConstructors();
methods = thisClass.getMethods();
fields = thisClass.getFields();
}
// output:
// This class is class Reflect
// Method 0 public static void
// Reflect.main(java.lang.String[])
// Method 1 public native int java.lang.Object.hashCode()
// Method 2 public final native java.lang.Class
// java.lang.Object.getClass()
// Method 3 public final void
// java.lang.Object.wait(long,int) throws
// java.lang.InterruptedException
// Method 4 public final void java.lang.Object.wait()
// throws java.lang.InterruptedException
// Method 5 public final native void java.lang.Object.wait(long)
// throws java.lang.InterruptedException
// Method 6 public boolean
// java.lang.Object.equals(java.lang.Object)
// Method 7 public java.lang.String java.lang.Object.toString()
// Method 8 public final native void java.lang.Object.notify()
// Method 9 public final native void
// java.lang.Object.notifyAll()
// Field 0 public static java.lang.reflect.Constructor[]
// Reflect.constructors
// Field 1 public static java.lang.reflect.Method[]
// Reflect.methods
// Field 2 public static java.lang.reflect.Field[]
// Reflect.fields
// Field 3 public static java.lang.Class Reflect.thisClass
This code is designed to peek into its own .class file and see what constructors,
methods and fields are available inside. The whole code can be written within the
main method itself, but this slightly contrived example has fields and a constructor
for illustration. Since all the classes inherit from Object the methods of that class
are also shown in the output.
Advantages of Reflection
1) Signature-Based Polymorphism
Java and C# programmers are familiar with polymorphism based on interface
inheritance. Reflection provides an alternative where we can invoke methods having
same signature, from different classes not have a common interface (which is
required in interface based polymorphism). For example, we can implement dynamic
observer pattern or command pattern (dynamic design patterns) which invoke a
method based on same signature from classes not related to each other by
interface inheritance.
2) Performance
The code that is reflective is slow that the direct code that performs the same
functionality. Reflection can be order of times (10 to 30 times) slower than the
direct code. Reflection performance also depends on the kind of operations are
done in the code (is it creating objects, accessing members, making method calls,
etc). If the main logic uses reflection or if the hot-spot function uses reflection,
then we can feel the effect of reflection on application performance. If the
reflection code is used sparingly in the application or if the methods using
reflection code are rarely invoked, then reflection might not affect application
performance.
3) Code Complexity
The reflective code is more complex and harder to understand the direct code that
performs the same functionality. There are three main problems that affect the
readability and maintainability of the code using reflection.
1) Obscures Logic
The use of reflection APIs obscures the underlying logic. In direct code for
achieving the same functionality, the logic will be straight-forward to understand.
However, since all the basic operations in reflection should be done using reflection
APIs, the logic gets lost in the maze of API calls.
For example, creating an object and invoking a method in that is small and trivial in
direct code. For achieving the same using reflection, the class has to be loaded, an
object has to be created by programmatically figuring out the correct constructor
and pass the necessary arguments, get the meta-data of the class and get the
Method object from that class, and then invoke that method on the object by
passing necessary arguments. All this requires multiple lines of code and a few
exception handlers to ensure that the code doesn’t crash in case if something goes
wrong in this process.
2) Missing Static Checks
The compiler statically checks the program when code is compiled. For example, the
compiler ensures that only valid methods are called based on the static type of the
object (to ensure type-safety). However, for equivalent reflective code, the
compiler only checks of the reflection APIs are statically correct or not. All the
checks involved in actually performing the operation using reflection APIs should be
done by the programmer to ensure that the program is type-safe and that we are
not violating any semantic rules.
For example, if we attempt to invoke bar() method on an object of Object type
created using reflection, a runtime exception will be thrown that the method is not
found in Object class. Similarly, if you try to access a private member from outside
the class, an exception indicating access violation will be thrown at runtime. These
two problems would have been caught at compile-time itself in direct code.
Consider another example where you’re invoking a method of a specific class using
reflection. If the signature of the method changes, then all the code that depends
on that method will fail to compile. However, the code that invokes the method
using reflection will not fail at compile time, but it will fail only at runtime.
3) More Exception Handlers
The static checks done by a compiler in static code have to be done by the
programmers using exception handling code. So, programmers have to provide more
exception handlers in reflective code than the direct code. Providing more
exception handling code increases implementation/testing effort and complicates
application logic, so there is more possibility of making mistakes in code.
Because of these reasons, it is more difficult to maintain or debug programs using
reflection compared to the direct code that does not use reflection.
4) Security
Programmers often cite security as a reason for not using reflection in their code.
It is true that there are some security concerns in using reflection, particularly in
component and internet programming where the code might be from un-trusted
sources. However, in most cases, the security concerns are misguided. The
reflection code is still executed under the same watchful eyes of the Java security
manager, like any other code. So, just using reflection in the code doesn’t affect
the safety of the code or make the code unsafe.
Having stated that, reflection does provide some limited ways to circumvent many
of the checks that are usually statically checked. For example, in Java, private
members are accessible only to the class members. If we attempt to access a
private member from code outside the class, it will result in an exception. But by
setting the suppressAccessChecks method (provided in
java.lang.reflect.ReflectPermission class), access checks can be switched off for
that class. So, you can access all members from outside the class using reflection,
which has potential for un-secure access to internal data of a class or an object.
For more details on security and performance problems with reflection, see [2].
References:
[1] Egbert Althammer, "Reflection Patterns in the Context of Object and
Component Technology", PhD thesis, Universität Konstanz, 2001. URL:
http://www.ub.uni-konstanz.de/kops/volltexte/2002/803/
[2] Dennis Sosnoski, ‘Java programming dynamics, Part 2: Introducing reflection’,
developerWorks, 2003. URL: http://www.ibm.com/developerworks/library/j-
dyn0603/
[3] Joshua Bloch, Effective Java Programming Language Guide, Addison-Wesley
Professional, 2001.
[4] Bill Wagner, "C#:50 Specific Ways to Improve Your C#", Addison-Wesley
Professional, 2004.