Type compatibility of Reflection Method Parameters

Source: Internet
Author: User

Java method calls follow the principle of proximity compatibility when passing parameters: if the actual parameter type and declared type do not match, Java will automatically find other methods that are compatible with the actual parameter type. The so-called "compatibility" means that the actual parameter type is inherited from the declared type, or if the declared type is an interface, the actual parameter type implements this interface (Note: This article only applies to the reference type, currently, compatibility between basic types is not considered)

The simplest example is as follows (taking constructor as an example ):

public class Test {public Test(List<?> arg1) {System.out.println("Instance Newed as List:    "+arg1.getClass());}public static void main(String[] args){new Test(new ArrayList<Object>());}}

 

Output:
Instance newed as list: Class java. util. arraylist

However, if you use the reflection mechanism to call a method, the parameter type must be exactly matched when obtaining the parameter:

public class Test {public Test(List<?> arg1) {System.out.println("Instance Newed as List:    "+arg1.getClass());}public static void main(String[] args){Class<?> clazz = Test.class;Constructor<?> constructor;ArrayList<String> arg = new ArrayList<String>();constructor = clazz.getDeclaredConstructor(arg.getClass());constructor.newInstance(arg);}}

 

When the above code is executed, an error "method not found" is reported:

Exception in thread "main" java.lang.NoSuchMethodException: test.Test.<init>(java.util.ArrayList)at java.lang.Class.getConstructor0(Unknown Source)at java.lang.Class.getDeclaredConstructor(Unknown Source)at test.Test.main

  

The reason is that the actual type of the ARG parameter is arraylist. Therefore, argS. getclass () returns arraylist. class instead of list. Class. Therefore, the reflection mechanism cannot find the matched constructor.
To solve this problem, you can modify the getdeclaredconstructor parameter:
Constructor = clazz. getdeclaredconstructor (list. Class );
The program runs normally and the output is as follows:
Instance newed as list: Class java. util. arraylist

However, the disadvantage is that the declared type list. class must be displayed in the Code, so that the method with polymorphism cannot be implemented. Is there a way to automatically match compatible methods based on actual parameter types like normal calls?
Let's take a look at the source code of the class. The following are several key methods related to the construction in the class:

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)throws NoSuchMethodException, SecurityException {checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader());return getConstructor0(parameterTypes, Member.DECLARED);}private Constructor<T> getConstructor0(Class[] parameterTypes,int which) throws NoSuchMethodException{Constructor[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));for (int i = 0; i < constructors.length; i++) {if (arrayContentsEq(parameterTypes,constructors[i].getParameterTypes())) {return getReflectionFactory().copyConstructor(constructors[i]);}}throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));}private static boolean arrayContentsEq(Object[] a1, Object[] a2) {if (a1 == null) {return a2 == null || a2.length == 0;}if (a2 == null) {return a1.length == 0;}if (a1.length != a2.length) {return false;}for (int i = 0; i < a1.length; i++) {if (a1[i] != a2[i]) {return false;}}return true;}

 

The public method getdeclaredconstructor is the entry to the reflection call. The checkmemberaccess method mainly checks the visibility of methods and parameters, which has little to do with this article.
The private method getconstructor0 is the core function. The private method privategetdeclaredconstructors is used to obtain the list of all (public) constructor methods. The core of privategetdeclaredconstructors is a native method implemented through the underlying layer. This article will not discuss its implementation mechanism. The class also has a public portal method getdeclaredconstructors, which is used to obtain the list of all constructor methods from the outside. This public portal essentially calls the privategetdeclaredconstructors method implementation function.
After obtaining the method list, find the method for parameter type Matching through the round robin list, and judge whether the matching is successful through the static method arraycontentseq. This method compares the corresponding elements of two object arrays (actually class arrays) one by one. The source that the reflection mechanism cannot implement type compatibility is here, because the arraycontentseq method directly compares two class objects with equal signs, the inheritance or interface implementation relationships are completely ignored.
After finding the problem, you can easily modify it. The class itself has a public native method isassignablefrom (class <?> CLs), when and only when class B inherits or implements Class A,. class. isassignablefrom (B. class) returns true, so in principle, you only need to set if (A1 [I] In arraycontentseq! = A2 [I (! A2 [I]. isassignablefrom (A1 [I.
Of course, we are not likely to rewrite the class, but we can follow the above three methods to write a method to obtain a specific class method:

public static Constructor<?> getCompatibleDeclaredConstructor(Class<?> clazz, Class<?>... parameterTypes)throws NoSuchMethodException {Constructor<?>[] constructors = clazz.getDeclaredConstructors();Constructor<?> compatibleConstructor = null;for (int i = 0; i < constructors.length; i++)if (parameterTypesCampatible(parameterTypes,constructors[i].getParameterTypes())&& ((compatibleConstructor == null) || parameterTypesCampatible(constructors[i].getParameterTypes(),compatibleConstructor.getParameterTypes())))compatibleConstructor = constructors[i];if (compatibleConstructor == null)throw new NoSuchMethodException(clazz.getName() + ".<init>"+ argumentTypesToString(parameterTypes));return compatibleConstructor;}private static boolean parameterTypesCampatible(Class<?>[] a1, Class<?>[] a2) {if (a1 == null)return a2 == null || a2.length == 0;if (a2 == null)return a1.length == 0;if (a1.length != a2.length)return false;for (int i = 0; i < a1.length; i++)if (!a2[i].isAssignableFrom(a1[i]))return false;return true;}

 

In getcompatibledeclaredconstructor, the class is passed as a parameter, so that the method of the class itself is avoided. The method follows the original idea, first through the class. the getdeclaredconstructors method obtains the constructor list and compares the parameter compatibility. The private method parametertypescampatible is used for the comparison, which is equivalent to the original class. the arraycontentseq method, but the compatibility conditions are relaxed.
In getcompatibledeclaredconstructor, we use a compatibleconstructor object to store the returned value. This object may be assigned multiple values in the round robin, in the case of polymorphism, multiple methods may be compatible with the actual parameter type. In the round robin, the IF statement's judgment condition exactly achieves the proximity compatibility principle.

The above Code also uses a static method argumenttypestostring used to output an error. This method can be copied from the class and has little to do with core functions. The implementation code is as follows:

private static String argumentTypesToString(Class<?>[] argTypes) {StringBuilder buf = new StringBuilder("(");if (argTypes != null) {for (int i = 0; i < argTypes.length; i++) {buf.append(i > 0 ? ", " : "");buf.append((argTypes[i] == null) ? "null" : argTypes[i].getName());}}return buf.append(")").toString();}

 

The following is the test code for the above method:

Public class test {public test (list <?> Arg1) {system. Out. println ("instance newed as list:" + arg1.getclass ();} public static constructor <?> Getcompatibledeclaredconstructor (); Private Static Boolean parametertypescampatible (); Private Static string argumenttypestostring (); public static void main (string [] ARGs) throws exception {// todo auto-generated method stubclass <?> Clazz = test. Class; constructor <?> Constructor; arraylist <string> Arg = new arraylist <string> (); system. out. println ("directly constructed:"); new test (New arraylist <Object> (); system. out. println ("compatible reflection structure:"); constructor = getcompatibledeclaredconstructor (clazz, Arg. getclass (); constructor. newinstance (ARG); system. out. println ("direct reflection structure:"); constructor = clazz. getdeclaredconstructor (Arg. getclass (); constructor. newinstance (ARG );}}

 

Output:

Direct construction:
Instance newed as list: Class java. util. arraylist
Compatible reflection structure:
Instance newed as list: Class java. util. arraylist
Direct reflection structure:
Exception in thread "Main" Java. Lang. nosuchmethodexception: Test. Test. <init> (Java. util. arraylist)
At java. Lang. Class. getconstructor0 (unknown source)
At java. Lang. Class. getdeclaredconstructor (unknown source)
At Test. Test. Main (test. Java: 100)

We can see that the modified reflection mechanism achieves the same functions as direct reference, and the original class. getdeclaredconstructor method directly reports an error.

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.