If you have ever asked yourself questions like these:
– “How do I invoke a method, having only its name in a String?”
- “How do I list all the attributes in a class dynamically?”
- “How do I write a method that resets the state of any given object to default values?”
- “How do I list all the attributes in a class dynamically?”
- “How do I write a method that resets the state of any given object to default values?”
Then you have probably already heared of Java’s Reflection API, and in case you haven’t, this is a good opportunity to see what it’s all about, and what it can be used for. This feature is really powerful, but as always, has to be used with some good judgement.
The advantage it brings to the table is the ability to analyse information regarding a class, as its attributes, methods, annotations, or even the state of an instance, all of that in runtime. The dynamism obtained by this sounds really useful, doesn’t it?
In this tutorial I intent to show some basic usage of the Reflection API. What can be done, what shouldn’t be done, advantages and drawbacks.
So… Shall we? ![:)](http://rodrigosasaki.com/wp-includes/images/smilies/icon_smile.gif)
![:)](http://rodrigosasaki.com/wp-includes/images/smilies/icon_smile.gif)
The starting point, the Class<T> class
One of the most important things when working with Reflection is knowing where to start, knowing what class gives us access to all that information. And the answer to that is: java.lang.Class<T>
Let’s assume we have the following class:
1
2
3
4
5
6
7
8
| package com.pkg; public class MyClass{ private int number; private String text; } |
We can obtain the Class‘s reference in 3 different ways. Directly from the class, from its name, or from an instance:
1
2
3
4
5
| Class<?> clazz1 = MyClass. class ; Class<?> clazz2 = Class.forName( "com.pkg.MyClass" ); MyClass instance = new MyClass(); Class<?> clazz3 = instance.getClass(); |
Tip: AHere we see an important detail. Normally name the identifiers of an instance of Class something like clazz or clz, it may seem weird, but that’s only because class is already a reserved word in the Java language.
From the class’ reference we can navigate through everything, find out what are its members, annotations, even its package or its ClassLoader, but we’re gonna get into all of that in more detail later, so for now let’s focus on the methods that give us information regarding class itself:
int getModifiers()
| Returns the modifiers of the class or interface inside an int, to find out exactly what modifiers are applied, we should use the static methods provieded by the Modifier class |
boolean isArray()
| Determines if the class represents an Array |
boolean isEnum()
| Determines if the class was declared as an enum in the sourcecode |
boolean isInstance(Object obj)
| Returns true if the informed object can be assigned to an object of the type represented by this Class |
boolean isInterface()
| Determines if the class represents an interface |
boolean isPrimitive()
| Determines if the class represents a primitive type |
T newInstance()
| Creates a new instance of this class |
Class<? super T> getSuperclass()
| Returns the reference of the superclass, in case it is invoked in the Object class, it returns null |
You can see a complete definition of these methods and many others directly in the class documentation.
Tip: To use successfully the methods in the Modifier class, it is necessary to have some basic knowledge on bitwise operations.Being the most common in this case, the AND operation.
The attributes, the Field class
The fields represent the attributes of a class, its that simple. It is through this class that we can obtain information about them, but before that, how do we get a reference to a Field?
Inside the Class class we have a few different methods that can return us the class’ fields, as a spoiler I already say that there are equivalent methods for methods and constructors, but let’s focus on theattributes first.
We have a few “important to remember” things in relation to how to find out the members of a class, I’ll present the methods first, and then elaborate more on these details.
Field getField(String name) | Returns the Field that reflects to the public attribute of the class with the given name. |
Field[] getFields() | Returns the array of Fields that reflect to the public attributes of the class. |
Field getDeclaredField(String name) | Returns the Field that reflects to the declared attribute of the class with the given name. |
Field[] getDeclaredFields() | Returns the array of Fields that reflect to the declared attributes of the class. |
Alright, seems like we have 2 groups of very similar methods, and we do. The differences between them are subtle, and not many know them.
The getField and getFields methods return only the public attributes of the class. It’s somehow cleaner, because we don’t always want (and many times shouldn’t) meddle with internal attributes. The advantage here is that it still searches for attributes in the superclass, going up the hierarchy, being convenient for us, but again, the attributes in the superclasses must be public to be found.
Now the getDeclaredField and getDeclaredFields methods are not so clean, for them any declared attribute is valid, may it be private, protected or whatever. So, contrary to popular belief, accessing private members via Reflection is not that complex. And as another difference they do not search for attributes on the superclasses, so they ignore inheritance hierarchy completely.
Let’s exemplify trying to access the attribute inside a class, showing the use of the aforementioned methods, and others that the Field class provides. Using as example the MyClass class we defined earlier, let’s assume we want to know the value contained in the text attribute of some instance.
1
2
3
4
5
6
| MyClass instance = new MyClass(); instance.setText( "Reflective Extraction" ); Class<?> clazz = instance.getClass(); Field field = clazz.getDeclaredField( "text" ); // Accessing private attribute Object value = field.get(instance); System.out.println(value); |
Tip: The usage of the get method may be confusing at first, but it’s rather simple. Having aField in your hands, you send the target instance from which you want to extract the value as an argument. It is necessary to have in mind that the Field is related to the class, and not to the instance, therefore if the attribute is not static, a concrete instance is required so that a value can be extracted. In case we’re talking about a static attribute, you can pass any instance of any type, or even null as an argument.
Okay, we accessed our attribute using the getDeclaredField method, passing the field’s name, and retrieving its current value using the get method. All done, right?
Wrong. When we run our code we see that an exception has been thrown, more specifically anIllegalAccessException, it’s not just because we have the field’s reference, that we can manipulate it to our will, but there’s a way around that as well.
The Field‘s superclass is the AccessibleObject class which is also the superclass of Method and Constructor, so this explanation is valid for any of the 3.
The AccessibleObject class defines 2 main methods: setAccessible(boolean) and isAccessible(), that basically define if an attribute is accessible or not. In our case, private attributes are never accessible, unless you’re inside the same class, in that case you can access them without any problems. So all we have to do is configure our text as accessible.
1
2
3
4
5
6
7
| MyClass instance = new MyClass(); instance.setText( "Reflective Extraction" ); Class<?> clazz = instance.getClass(); Field field = clazz.getDeclaredField( "text" ); // Accessing private attribute field.setAccessible( true ); // Setting as accessible Object value = field.get(instance); System.out.println(value); |
And there we go, we can print our value without hassle ![:)](http://rodrigosasaki.com/wp-includes/images/smilies/icon_smile.gif)
![:)](http://rodrigosasaki.com/wp-includes/images/smilies/icon_smile.gif)
Tip: The AccessibleObject class has a convenience static method, also named setAccessible, but it receives an array of AccessibleObject and the boolean flag. This method can be used to set multiple Fields, Methods or Constructors as accessible all in one go. In case you want to set all the attributes in a class as accessible, you can just do this:
1
2
3
4
| MyClass instance = new MyClass(); Class<?> clazz = instance.getClass(); Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true ); |
And in the same way we extract the value from an attribute, we can assign one too. We do that using the set method. When we invoke it we need to inform the instance we want to modify, and the value we want to set to the corresponding attribute.
1
2
3
4
5
6
| MyClass instance = new MyClass(); Class<?> clazz = instance.getClass(); Field field = clazz.getDeclaredField( "text" ); field.setAccessible( true ); field.set(instance, "Reflective attribution" ); System.out.println(instance.getText()); |
Actions, the Method class
Before we get started on the actual methods, I want to highlight, as I said before, that it is very similar to the Field and Constructor classes, so these 3 may be obtained in the same way from our Class reference, which means that if we have a getDeclaredField method we also have a getDeclaredMethod and agetDeclaredConstructor (the same is true for the other methods). So they work exactly the same way and for that reason explaining them again would be pointless and time wasting.
So let’s get started, how can we invoke methods using Reflection?
A method is not like an attribute, so only it’s name isn’t enough, because we can have many different methods with the same name, which is called overloading. So to get a specific method we need to inform it’s name, and the list of arguments it receives. Let’s assume our class has the methods described below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| public void print(String text){ System.out.println(text); } public void printHelloWorld(){ System.out.println( "Hello World" ); } public int sum( int [] values){ int sum = 0 ; for ( int n : values){ sum += n; } return sum; } |
To get the reference to these methods, in the order they were declared in the example, we could just do this:
1
2
3
4
5
| MyClass instance = new MyClass(); Class<?> clazz = instance.getClass(); Method print = clazz.getMethod( "print" , String. class ); Method printHelloWorld = clazz.getMethod( "printHelloWorld" ); Method sum = clazz.getMethod( "sum" , int []. class ); |
And to invoke them, we use the invoke method (coincidence?
)
![:)](http://rodrigosasaki.com/wp-includes/images/smilies/icon_smile.gif)
The method’s signature is this:
Object invoke(Object obj, Object... args)
Which means, we need to inform the instance (target) on which the method will be invoked as well as the arguments that we need to pass to it, if any. Besides that it returns an Object, that will be the return of the method, whatever it may be. In case the method returns nothing (void), a call to invoke will return null.
For our 3 method references above the invocations would be like this:
1
2
3
4
| print.invoke(instance, "I'm Mr. Reflection by now" ); printHelloWorld.invoke(instance); int sumValue = ( int ) sum.invoke(instance, new int []{ 1 , 4 , 10 }); System.out.println(sumValue); |
Tip: Again if we’re working with static methods, it’s not necessary to pass a valid instance, we can just do this:
1
| staticMethod.invoke( null ); |
What about generic arguments?
Well, in that case we need to understand a little more about the language before we can invoke them. It’s necessary to know that generic types only exist in compilation time, and as Reflection runs in runtime, the generic types no longer exist, all of this because of a feature called Type Erasure, that was created to maintain backwards compatibility with previous Java versions.
Most likely, a method that receives a generic argument will receive Object, but there’s also another possiblity. If you declare a generic type like this: <T extends CharSequence>, it is possible that the method in runtime will receive a CharSequence, since it’s the most specific type possible, still remaining generic, so for this method:
1
2
3
| public print(T sequence){ System.out.println(sequence) } |
A Reflection invocation could look like this:
1
2
| Method print = clazz.getMethod(print, CharSequence. class ); print.invoke(instance); |
Creating instances the Constructor<T> class
As we all know, a constructor is not a method, even though its usage is very similar. It’s like we’re invoking a method, but with the new keyword behind it, and also we only invoke it when we want to create a new instance of the class. This resemblance in usage causes a resemblance in its manipulation regarding Reflection. It so happens that instead of using invoke, we’re going to use the newInstance method, with a few differences.
We are creating an instance, so there’s no instance to associate to the constructor, therefore we don’t pass any as argument. But as we do with our Methods, we pass the constructor’s argument list.
The Class<T> has a newInstance method too, but it would only be useful if our class had an accessible constructor without arguments, in case there isn’t any, we need a direct reference to our Constructor<T>. Let’s define a few constructors in our example class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| public class MyClass{ private String text; private int number; public MyClass(){ } public MyClass(String text){ this .text = text; } public MyClass(String text, int number){ this .text = text; this .number = number; } } |
Now let’s get each one of them using Reflection:
1
2
3
4
| Class clazz = MyClass. class ; Constructor c1 = clazz.getConstructor(); Constructor c2 = clazz.getConstructor(String. class ); Constructor c3 = clazz.getConstructor(String. class , int . class ); |
Now let’s create 3 instances, one from each constructor reference:
1
2
3
| MyClass instance1 = c1.newInstance(); MyClass instance2 = c2.newInstance( "text" ); MyClass instance3 = c3.newInstance( "other text" , 1 ); |
Getting this far we can see that we can manipulate a class almost any way we want, listing its attributes, getting references to methods, altering its state, reading information, but we’re still missing one thing, that in my opinion, is too cool not to mention ![:)](http://rodrigosasaki.com/wp-includes/images/smilies/icon_smile.gif)
![:)](http://rodrigosasaki.com/wp-includes/images/smilies/icon_smile.gif)
Metadata, the AnnotatedElement interface
When we need to check information related to annotations, we use the methods provided by theAnnotatedElement interface. FYI: the first class in the hierarchy to implement these methods isAccessibleObject, so all its subclasses will have access to them.
With annotations we can define information about our classes, its attributes and/or methods, without actually writing any execution code. The annotation will be processed in another time, in a way making development easier. So let’s get to it and write ourselves a small example:
We create 1 annotation named @NotNull, and whenever an attribute is annotated with it, it cannot hold the value null, ’nuff said. Let’s get to the code::
1
2
3
4
5
6
| public class MyClass{ @NotNull private String text; } |
Okay, so we defined this characteristic about our text attribute, but how are we going to effectively validate this rule? With our Validator class, like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| public class Validator{ public static void validate(Object target) throws IllegalArgumentException, IllegalAccessException{ Class<?> clazz = target.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields){ validateField(field, target); } } private static void validateField(Field field, Object target) throws IllegalArgumentException, IllegalAccessException{ field.setAccessible( true ); Object value = field.get(target); if (field.isAnnotationPresent(NotNull. class )){ if (value == null ) throw new IllegalArgumentException( "This attribute cannot be null" ); } } } |
So when we validate our objects, we can use this annotation to work for us, and our validator will check it, keeping the code in a single place, making it that much more maintainable. In some point of our code we’ll have a call like this:
1
| Validator.validate(myClassInstance); |
And we’re done. This technique is widely used when developing frameworks, and you can usually just check the documentation to see what each annotation would do, and use them accordingly.
Drawbacks, why shouldn’t I use Reflection?
I believe it was made clear that using reflection brings many benefits and conveniences for us in some situations, but as we all know, when all we have is a hammer, any problem looks like a nail, so don’t get all excited thinking about how to solve everything with Reflection, because it has its disadvantages:
- Performance overhead: When using reflection, the JVM needs to do a lot of work to get all the information we need, do the dynamic invocations and all, so this carries a certain cost in processing time.
- Runtime safety: In order to run Reflection code it is necessary to have a certain level of clearence inside the virtual machine, and you may not have that all the time, so keep that in mind when thinking about using it.
- Model safety: Our attributes have different visibilities for a reason, right? And reflection can completely ignore them, doing whatever it wants, so it may cause some alerts in your encapsulation.
The basic rule is: Only use Reflection when there’s no easier alternative. If you can do something without reflection, you probably should. You should analyse on a case by case basis.
More information can be obtained in the Oracle tutorial.
I know there are other features when talking about Reflection, such as Proxies, but they are much more advanced and complex. I may write about them in a future post, but its out of the scope of this one, as it is only meant to be used as an introduction to the subject, or a refresher for those who already knew this, so bringing up advanced topics would do more harm than good.
I hope you all enjoyed it, see you next time!
Немає коментарів:
Дописати коментар