The right approach to using MethodHandleProxies

In the Java project I'm currently working on, I dynamically load classes, and then use the reflection API to find and execute methods on those classes that have specific annotations.

The code that does the actual execution works exclusively from the point of view of the functional interfaces of Java-8 (for compatibility reasons), so I need to have an intermediate step in which Method, detected by reflection, they are converted to the corresponding functional interfaces. I achieve this using the class MethodHandleProxies.

For compatibility reasons, the functional interfaces again considered are common interfaces. This triggers an “unverified conversion” warning when using the method MethodHandleProxies.asInterfaceInstance, as this method returns a bare interface.

The following is a brief example in which the main steps are reproduced:

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.Arrays;

public class TestClass {
    private String prefix;

    public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException, SecurityException {
        // Use reflection to find method.
        Method method = Arrays.stream(TestClass.class.getDeclaredMethods()) // Stream over methods of ConsumerClass
                .filter(m -> m.isAnnotationPresent(Marker.class)) // Retain only methods with @Marker annotation
                .findFirst().get(); // Get first such method (there is only one in this case)

        // Convert method to "MethodInterface" functional interface.
        MethodHandle handle = MethodHandles.lookup().unreflect(method);
        MethodInterface<TestClass, String> iface = MethodHandleProxies.asInterfaceInstance(MethodInterface.class, handle);

        // Call "testMethod" via functional interface.
        iface.call(new TestClass("A"), "B");
    }

    public TestClass(String prefix) {
        this.prefix = prefix;
    }

    @Marker
    public void testMethod(String arg) {
        System.out.println(prefix + " " + arg);
    }

    @Retention(RUNTIME)
    public @interface Marker { }

    @FunctionalInterface
    public interface MethodInterface<I,V> {
        void call(I instance, V value);
    }
}

This code compiles and runs, but has a warning about an unverified conversion on assignment iface.

Creating a MethodInterfacenon-generic would solve this specific problem, but would mean that it would no longer work with method references for arbitrary types (which is desirable for other parts of the code).

For example, with the above definitions TestClass, the MethodInterfacefollowing line compiles:

MethodInterface<TestClass,String> iface = TestClass::testMethod;

MethodInterface :

@FunctionalInterface
public interface MethodInterface {
    void call(Object inst, Object value);
}

TestClass::testMethod , .

, :

  • .
  • @SuppressWarnings.
  • .

, ( ), 1. 2 , " ", . , .

, ?

+4
3

- , , . MethodHandleProxies . , , , , , , ( ) .

-, . interface Specific extends MethodInterface<TestClass,String> {}, , , , - .

, , LambdaMetafactory MethodHandleProxies. , ( JRE), .

// Use reflection to find method.
Method method = Arrays.stream(TestClass.class.getDeclaredMethods())
        .filter(m -> m.isAnnotationPresent(Marker.class))
        .findFirst().get();

// Convert method to "MethodInterface" functional interface.
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.unreflect(method);
MethodInterface<TestClass, String> iface;
try {
    iface = (MethodInterface<TestClass, String>)LambdaMetafactory.metafactory(lookup,
            "call", MethodType.methodType(MethodInterface.class),
            MethodType.methodType(void.class, Object.class, Object.class),
            handle, handle.type())
          .getTarget().invoke();
} catch(RuntimeException|Error|ReflectiveOperationException|LambdaConversionException ex) {
    throw ex;
}
catch (Throwable ex) {
    throw new AssertionError(ex);
}
// Call "testMethod" via functional interface.
iface.call(new TestClass("A"), "B");

, . , , MethodHandleProxies, , , , , .

+3

, , , , , :

Class<MethodInterface> targetType = MethodInterface.class;
Function<Object,MethodInterface<TestClass,String>> casting=targetType::cast;

MethodInterface<TestClass, String> iface = casting.apply(
     MethodHandleProxies.asInterfaceInstance(targetType, handle)
);
+2

, , :

  MethodInterface.class

Class<MethodInterface>. , :

Class<MethodInterface<<TestClass, String>>> clazz =  
             MethodInterface<TestClass, String>.class;

- .

, ( ):

 String result = MethodHandleProxies.asInterfaceInstance(String.class, handle);

, .

+2

Source: https://habr.com/ru/post/1677734/


All Articles