Method handle used in JDK

preface Java can get type information at run time through reflection, but its disadvantage is that it is slow to execute...
1.MethodHandle
2.MethodType
3.Lookup
1. Ordinary call
2. Static method call
3. Call constructor
4. Call Getter method
5. Call Setter method
6. Call through Method
7. Difference between invoke and invokeExact

preface

Java can get type information at run time through reflection, but its disadvantage is that it is slow to execute. Therefore, another set of API MethodHandle is provided from Java 7. Its function is similar to reflection, which can access type information at run time, but it is said that its execution efficiency is higher than reflection, which is also called modern reflection of Java.

1, Several important classes

1.MethodHandle

It is a reference to the type of Method (or domain, constructor, etc.) that can be executed directly, or it is an object capable of safely calling a Method. In other words, through the handle, we can directly call the underlying Method referenced by the handle. In terms of function, the Method handle is similar to the Method class in reflection, but the Method handle has more powerful function, more flexible use and better performance.

2.MethodType

It is an immutable object that represents a method signature type. Each method handle has a MethodType instance that indicates the return type and parameter type of the method. Its type is completely determined by the parameter type and method type, and has nothing to do with the name and class of the underlying method it references. For example, the type of the method handle of the length method of the String class and the intValue method of the Integer class are the same. Because both methods have no parameters and the return value type is int, we can obtain the same method type through the following statement: MethodType MT = MethodType. MethodType (int.class); Object instances of MethodType can only be created through static factory methods in MethodType class, and all object instances of MethodType class are immutable, similar to String class. If the information in the MethodType instance is modified, another MethodType instance will be generated.

3.Lookup

The creation factory of MethodHandle, through which MethodHandle can be created. It is worth noting that the inspection is processed at creation time, not at call time.

2, Use steps

There are four steps to using method handles:
(1) Create Lookup
(2) Create MethodType
(3) Get MethodHandle based on Lookup and MethodType
(4) Call MethodHandle
The following tests the use of method handles in several cases

1. Ordinary call

Create a tool method to get (my class name here is ReflectUtils)

/** * Get MethodHandle * @version 1.0 * @since jdk1.8 * @date 2021/10/27 * @param returnClass Return value Class object - void.class * @param clazz The class in which the method resides * @param methodName Method name * @param parameterClasses Method parameter type * @throws * @return java.lang.invoke.MethodHandle */ public static MethodHandle getMethodHandle(Class returnClass, Class clazz, String methodName, Class ... parameterClasses) throws NoSuchMethodException, IllegalAccessException { MethodType methodType = MethodType.methodType(returnClass, parameterClasses); return MethodHandles.lookup().findVirtual(clazz, methodName, methodType); }

Test:

public class MethodHandleTests { ....... public void test(String name) { System.out.println(MessageFormat.format("Hello ", name)); } //The test calls a normal function @Test public void _test() throws Throwable { MethodHandle methodHandle = ReflectUtils.getMethodHandle(void.class, MethodHandleTests.class, "test", String.class); methodHandle.invokeExact(new MethodHandleTests(), "Zhang San"); } ...... }

2. Static method call

Create a tool method to get (my class name here is ReflectUtils)

/** * Gets the MethodHandle of the static method * @version 1.0 * @since jdk1.8 * @date 2021/10/31 * @param returnClass Return value Class object - void.class * @param clazz The class in which the method resides * @param methodName Method name * @param parameterClasses Method parameter type * @throws * @return java.lang.invoke.MethodHandle */ public static MethodHandle getStaticMethodHandle(Class returnClass, Class clazz, String methodName, Class ... parameterClasses) throws NoSuchMethodException, IllegalAccessException { MethodType methodType = MethodType.methodType(returnClass, parameterClasses); return MethodHandles.lookup().findStatic(clazz, methodName, methodType); }

Test:

public class MethodHandleTests { ....... public static void testStatic(String name) { System.out.println(MessageFormat.format("Hello ", name)); } //The test calls a static function @Test public void _testStatic() throws Throwable { MethodHandle methodHandle = ReflectUtils.getStaticMethodHandle(void.class, MethodHandleTests.class, "testStatic", String.class); methodHandle.invokeExact("Zhang San"); } ...... }

3. Call constructor

Create a tool method to get (my class name here is ReflectUtils)

/** * Gets the MethodHandle of the constructor * @version 1.0 * @since jdk1.8 * @date 2021/10/31 * @param clazz Constructor class * @param parameterClasses Constructor parameters * @throws * @return java.lang.invoke.MethodHandle */ public static MethodHandle getConstructMethodHandle(Class clazz, Class ... parameterClasses) throws NoSuchMethodException, IllegalAccessException { MethodType methodType = MethodType.methodType(void.class, parameterClasses); return MethodHandles.lookup().findConstructor(clazz, methodType); }

Test:

public class MethodHandleTests { ....... //Test using constructor @Test public void _testConstruct() throws Throwable { MethodHandle constructMethodHandle1 = ReflectUtils.getConstructMethodHandle(MethodHandleTests.class); Object methodHandle1 = constructMethodHandle1.invoke(); System.out.println(methodHandle1); } ...... }

4. Call Getter method

Create a tool method to get (my class name here is ReflectUtils)

/** * Get Getter MethodType * @version 1.0 * @since jdk1.8 * @date 2021/10/31 * @param clazz class * @param fieldName Member variable name * @throws * @return java.lang.invoke.MethodHandle */ public static MethodHandle getGetterMethodHandle(Class clazz, String fieldName) throws IllegalAccessException, NoSuchFieldException { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); return MethodHandles.lookup().unreflectGetter(field); }

Class tested:

package com.iscas.common.tools.core.reflect.reflectTest; import java.util.List; import java.util.Map; /** * * @author zhuquanwen * @vesion 1.0 * @date 2018/7/14 23:00 * @since jdk1.8 */ public class A { private static long xxx = 2; private List<A2> a2List; private Map<String, A2> map; private A1 a1; public int getA1Hash(){ return a1.hashCode(); } public String xxx(String str1, int str2, float[] str3){ StringBuilder sb = new StringBuilder(); sb.append(str1).append(str2).append(str3); return sb.toString(); } public List<A2> getA2List() { return a2List; } public void setA2List(List<A2> a2List) { this.a2List = a2List; } public Map<String, A2> getMap() { return map; } public void setMap(Map<String, A2> map) { this.map = map; } public A1 getA1() { return a1; } public void setA1(A1 a1) { this.a1 = a1; } }
package com.iscas.common.tools.core.reflect.reflectTest; import java.util.List; /** * * @author zhuquanwen * @vesion 1.064 23:01 * @since jdk1.8 */ public class A1 { private List<A11> a11List ; public List<A11> getA11List() { return a11List; } public void setA11List(List<A11> a11List) { this.a11List = a11List; } }

Test:

//Test getter @Test public void _testGetter() throws Throwable { MethodHandle constructMethodHandle1 = ReflectUtils.getGetterMethodHandle(A.class, "a1"); A a = new A(); a.setA1(new A1()); Object a1 = constructMethodHandle1.invoke(a); System.out.println(a1); }

5. Call Setter method

Create a tool method to get (my class name here is ReflectUtils)

/** * Get Setter MethodHandle * @version 1.0 * @since jdk1.8 * @date 2021/10/31 * @param clazz Constructor class * @param fieldName Member variable name * @throws * @return java.lang.invoke.MethodHandle */ public static MethodHandle getSetterMethodHandle(Class clazz, String fieldName) throws IllegalAccessException, NoSuchFieldException { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); return MethodHandles.lookup().unreflectSetter(field); }

Test:

//Test setter @Test public void _testSetter() throws Throwable { MethodHandle constructMethodHandle1 = ReflectUtils.getSetterMethodHandle(A.class, "a1"); A a = new A(); constructMethodHandle1.invoke(a, new A1()); System.out.println(a.getA1()); }

6. Call through Method

Create a tool method to get (my class name here is ReflectUtils)

/** * Get MethodHandle through Method * @version 1.0 * @since jdk1.8 * @date 2021/10/31 * @param method * @throws * @return java.lang.invoke.MethodHandle */ public static MethodHandle getMethodHandle(Method method) throws IllegalAccessException, NoSuchFieldException { return MethodHandles.lookup().unreflect(method); }

Test:

//Get MethodHandler through Method @Test public void _testMethod() throws Throwable { Method method = MethodHandleTests.class.getDeclaredMethod("test", String.class); MethodHandle methodHandle = ReflectUtils.getMethodHandle(method); methodHandle.invoke(new MethodHandleTests(), "Zhang San"); }

7. Difference between invoke and invokeExact

The difference between invoke and invokeExact methods is that the latter has higher accuracy or stricter requirements. The invokeExact method requires strict type matching when called. The invoke method allows for a more relaxed invocation. It will try to convert the return value and parameter type at the time of call. This is done through the asType method of the MethodHandle class. The function of the asType method is to adapt the current method handle to the new MethodType and generate a new method handle. When the type of the method handle at the time of calling is completely consistent with its declared type, calling the invoke method is equivalent to calling the invokeExact method; Otherwise, the invoke method will first call the asType method to try to adapt to the type at the time of the call. If the adaptation is successful, the call can continue. Otherwise, relevant exceptions will be thrown. This flexible adaptation mechanism makes the invoke method the method handle call method that should be used in most cases.
The basic rule of type matching is to compare whether the return value type and the type of each parameter can match each other. Assuming that the source type is S and the target type is T, the basic rules are as follows:
1. It can be done through java type conversion, generally from subclass to parent class, such as from String to Object type;
2. This can be done by converting basic types. You can only expand the type range, such as switching from int to long;
3. This can be done through basic types of automatic boxing and unpacking mechanisms, such as from int to Integer;
4. If S has a return value type and T'S return value type is void, the return value of S will be discarded.
5. If the return value of S is void and the return value of T is a reference type, the return value of T will be null;
6. If the return value of S is void and the return value of T is the basic type, the return value of T will be 0;
Test:

public String testExact(String name) { return "Hello " + name; } //Test invoke and invokeExact @Test public void _testExact() throws Throwable { MethodHandle methodHandle = ReflectUtils.getMethodHandle(String.class, MethodHandleTests.class, "testExact", String.class); MethodHandleTests methodHandleTests = new MethodHandleTests(); Object str1 = methodHandle.invoke(methodHandleTests, "Zhang San"); System.out.println("The return value type is automatically converted to Object>>>>" + str1); try { Object str2 = methodHandle.invokeExact(methodHandleTests, "Zhang San"); } catch (Throwable e) { System.out.println(">>>>Cannot automatically return value String Turn into Object"); } Object str3 = (String) methodHandle.invokeExact(methodHandleTests, "Zhang San"); System.out.println("invokeExact A strong conversion needs to be called>>>>" + str3); }
summary

In general, like reflection, it is far from the daily development of application programmers, but it will be widely used in the development of frameworks and toolkits.

31 October 2021, 09:15 | Views: 7920

Add new comment

For adding a comment, please log in
or create account

0 comments