Some research and views on generics and reflection

Source: Internet
Author: User

It is interesting to study the relationship between generics and reflection.
We know that reflection and generics are both dynamic Java technologies. Unlike inheritance and polymorphism, it is an object-oriented technology. It can be said that reflection and generics are all generated to make up for the shortcomings of object-oriented technologies such as inheritance and polymorphism. Most of the modes are built on the basis of object-oriented technology. Therefore, each mode is more or less dynamic, or lacks scalability. We have combined reflection and generics to expand the modes, make it more dynamic to meet our requirements.
In the process of combining these technologies, we will more or less think of combining generics and reflection. This is a very interesting topic: Both paradigm and reflection make Java have a certain degree of scalability, but at the same time they all have their own shortcomings, and they combine these two technologies, is it possible to solve their respective shortcomings so that they can be upgraded to a dynamic level?
As mentioned above, generics and reflection can promote each other. Let's take a look at how generics help reflection.
We know that the biggest headache of using reflection is that after reflection is applied, all objects of the Object type are obtained. to use such objects, we need to perform forced type conversion. As we know, one of the generic functions is to eliminate forced type conversion.
1. initialize objects during runtime
Initialization objects during runtime are our most common reflection functions, but the Object types we get through reflection during runtime are usually Object types, this object requires forced type conversion when we use it. Now, with reflection, we don't need to force type conversion.
Suppose we already have two classes:
Public class Cls1 {
 
Public void do1 (){
// TODO Auto-generated method stub
System. out. println ("cls1 ...");
 
}
 
}
Public class Cls2 {
 
Public void do2 (){
// TODO Auto-generated method stub
System. out. println ("cls2 ...");
}
 
}
We need to initialize these two objects at runtime. We can design the following initialization methods:
Public class Factory {
Public static <U extends Object> U getInstance (String clsName)
{
Try
{
Class <?> Cls = Class. forName (clsName );
Return (U) cls. newInstance ();
}
Catch (Exception e)
{
E. printStackTrace ();
Return null;
}
}
}
In this method, we actually use generics to implement forced type conversion in advance in the initialization method, so that we do not have to perform forced type conversion during use.
Their test code is as follows:
Cls1 i1 = Factory. getInstance ("fanxing. factory. dynaFactory. Cls1 ");
I1.do1 ();
Cls2 i2 = Factory. getInstance ("fanxing. factory. dynaFactory. Cls2 ");
I2.do2 ()
Test results:
Cls1...
Cls2...
Note that there are several problems with using this method:
First, return (U) cls. newInstance (); this statement has a warning error. Fortunately, this error is not a compilation error, but we can afford it.
Second, the compiler cannot perform type checks, such as Cls1 i1 = Factory. getInstance ("fanxing. factory. dynaFactory. cls1 "); Cls2 i1 = Factory. getInstance ("fanxing. factory. dynaFactory. cls1 "); can also be compiled in the past. Errors only occur during running.
 
In addition to the above method, there is also a better method.
This method requires that the object we pass in during the runtime be a Class object, of course, a generic Class object, as shown below:
Class <Cls1> cls = Cls1.class;
Try
{
Intf1 obj = cls. newInstance ();
Obj. do1 ();
}
Catch (Exception e)
{
E. printStackTrace ();
}
We can clearly see that there is no forced type conversion here, and of course there are no problems with the first method.
The running result is:
Cls1...
Based on this method, we can transform the previous initialization method:
Public static <U extends Object> U getInstance (Class <U> cls)
{
Try
{
Return cls. newInstance ();
}
Catch (Exception e)
{
E. printStackTrace ();
Return null;
}
}
Let's perform the following tests:
Cls1 c1 = Factory. getInstance (Cls1.class );
C1.do1 ();
Test results:
Cls1...
At this time, if we change the above test code:
Cls2 c1 = Factory. getInstance (Cls1.class );
The compilation error occurs. We can see that our second method does avoid the weakness of the first method, but the advantage of the first method is that, you only need to enter the class name as the parameter in the initialization method.
 
2. Call methods during runtime
Anyone who has used reflection knows that the result obtained after calling the method during the runtime will also be an Object. Such a result is tolerable in general method reflection. But sometimes it is intolerable. For example, we want to reflect the iterator () method of the List <T> class.
In general, we can do this using reflection:
Try
{
Class <?> Cls = Class. forName ("java. util. ArrayList ");
Method m = cls. getDeclaredMethod ("iterator", new Class [0]);
Return m. invoke (this, new Object [0]);
}
Catch (Exception e)
{
E. printStackTrace ();
Return null;
}
Of course, an Object is returned here, and the actual return type of the List <T> class iterator () is T. Obviously, we can modify the above Code as follows:
Try
{
Class <?> Cls = Class. forName ("java. util. ArrayList ");
Method m = cls. getDeclaredMethod ("iterator", new Class [0]);
Return (T) m. invoke (this, new Object [0]);
}
Catch (Exception e)
{
E. printStackTrace ();
Return null;
}
Similarly, our code will also encounter warning errors. But we can ignore it.
 
3. initialize array objects during runtime
Similarly, the array initialized during the runtime is also an Object. As shown in the following figure:
Object o = Array. newInstance (int. class, 10 );
If I want to get an array object in the getArray () method, I will get the code like the following:
Public Object getArray (Class cls, int size)
{
Return Array. newInstance (cls, size );
}
This obviously won't satisfy us. Because if we enter a Class <T> type parameter, we want to return a T type result.
With this idea, we use generics to modify the getArray () method as follows:
Public T [] getArray (Class <T> cls, int size)
{
Return (T []) Array. newInstance (cls, size );
}
Such modification will give us a satisfactory result. Similarly, the above Code will also get a warning error. However, our results were greatly optimized using generics.
 
The above examples show that generics can help reflection and greatly optimize reflection. At the same time, reflection does not passively accept the help of generics, and reflection can also help generics. This is the basic working principle based on reflection: Get the metadata of the data. That is to say, you can get generic metadata through reflection. In addition, reflection helps to initialize generic data during runtime.
The following is a simple example.
 
4. Use reflection to initialize generic classes
We usually use generics like this in a class:
Public final class Pair <A, B> {
Public final A fst;
Public final B snd;
 
Public Pair (A fst, B snd ){
This. fst = fst;
This. snd = snd;
}
......
}
This is of course our most basic usage, but it is often the case that the compiler wants to know more information about this unknown object, such as A fst, we can call some methods at runtime. It's easy. We can use this unknown type as the parameter input. Well, that's right. With this parameter, we need to use reflection to call its method during runtime.
I will not give this example here. Here is a simple example of the constructor that needs to be called for generic class initialization.
For the above Pair <A, B> Class, if the input parameter type of the constructor is not A and B, but Class <A> and Class <B>, so we have to use reflection in the constructor.
Public Pair (Class <A> typeA, Class <B> typeB ){
This. fst = typeA. newInstance ();
This. snd = typeB. newInstance ();
......
}
It can be seen that we can use the reflection tool like normal for unknown type parameters in generics. That is, you can perform reflection on these unknown parameters through reflection.
 
5. Use reflection to obtain generic information
This summary is even more interesting.
We still use the above Pair <A, B> as an example. If we use this class in A class, as shown below:
Public Class PairUser
{
Private Pair <String, List> pair;
......
}
If we apply reflection to the PairUser class, we can easily obtain some information about the pair attribute of the class, such as whether it is private or public, and its type.
If the pair attribute type is Pair through reflection, we know that this class is a generic class, so we want to know more about this generic class. For example, the class name of a generic class, the information of a generic parameter, and so on.
For the PairUser class example above, I want to know some information about its property pair, such as its type name and generic parameter type, such as String and List. The work to be done is as follows:
First, obtain this attribute.
Field field = PairUser. class. getDeclaredField ("pair ");
Then retrieve the generic type of the attribute.
Type gType = field. getGenericType ();
Determine whether the gType is of the ParameterizedType type. If yes, convert it to a variable of the ParameterizedType type.
ParameterizedType pType = (ParameterizedType) gType;
Get Original Type
Type rType = pType. getRawType ();
Then, you can use rType. getClass (). getName () to obtain the type name of the property pair.
Finally, obtain the parameter information.
Type [] tArgs = pType. getActualTypeArguments ();
You can use tArgs [j]. getClass (). getName () to obtain the type name of the generic parameter of the pair attribute.
The complete code is as follows:
Try
{
Field field = PairUser. class. getDeclaredField ("pair ");
Type gType = field. getGenericType ();
If (gType instanceof ParameterizedType)
{
ParameterizedType pType = (ParameterizedType) gType;
Type rType = pType. getRawType ();
System. out. println ("rawType is instance of" +
RType. getClass (). getName ());
System. out. println ("(" + rType + ")");
Type [] tArgs = pType. getActualTypeArguments ();
System. out. println ("actual type arguments are :");
For (int j = 0; j <tArgs. length; j ++ ){
System. out. println ("instance of" +
TArgs [j]. getClass (). getName () + ":");
System. out. println ("(" + tArgs [j] + ")");
}
}
Else
{
System. out. println ("getGenericType is not a ParameterizedType! ");
}
}
Catch (Exception e)
{
E. printStackTrace ();
}
 
}
Output result:
RawType is instance of java. lang. Class
(Class Pair)
Actual type arguments are:
Instance of java. lang. Class:
(Class java. lang. String)
Instance of java. lang. Class:
(Interface java. util. List)

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.