gpt4 book ai didi

java - 使用 MethodHandleProxies 的正确方法

转载 作者:搜寻专家 更新时间:2023-11-01 01:49:42 25 4
gpt4 key购买 nike

在我目前正在处理的 Java 项目中,我动态加载类,然后使用反射 API 来查找和执行那些具有特定注释的类的方法。

执行实际执行的代码仅根据 Java-8 功能接口(interface)(出于兼容性原因)工作,因此我需要有一个中间阶段,其中 Method使用反射发现的实例被转换为适当的功能接口(interface)。我使用 MethodHandleProxies 实现了这一点类。

再次出于兼容性原因,所讨论的功能接口(interface)是通用接口(interface)。这会在使用 MethodHandleProxies.asInterfaceInstance 方法时导致“未经检查的转换”警告,因为该方法返回“裸”接口(interface)。

以下是一个重现所涉及的主要步骤的简短示例:

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);
}
}

此代码编译并运行,但在分配给 iface 时有一个未经检查的转换警告。

使 MethodInterface 成为非泛型将解决这个特定问题,但意味着它不再适用于任意类型的方法引用(这对代码的其他部分来说是可取的)。

例如,对于 TestClassMethodInterface 的上述定义,下面的行编译:

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

但是,更改为 MethodInterface 的以下定义会破坏此:

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

TestClass::testMethod 分配给此接口(interface)的实例无法编译,因为参数类型错误。

在我看来,我有三个选择:

  1. 接受警告。
  2. 在作业中添加一个 @SuppressWarnings 注释。
  3. 提出另一种类型安全的方法。

我尽量确保我的代码不会生成警告(以尽量减少出现错误的机会),所以我不喜欢选项 1。选项 2 感觉就像它只是“掩盖裂缝”,但可以接受如果绝对必要。所以我的首选是想出一个不同的方法。

有没有其他方法本质上是类型安全的?

最佳答案

将反射生成的实例分配给参数化通用接口(interface)未经检查的操作,因为无法确保生成的类满足该参数化接口(interface)。其实MethodHandleProxies后面的实现根本不关心这个签名。所以有一个警告是正确的,当你确信你做的一切都是正确的时候抑制它,将抑制限制在尽可能小的范围内,是最好的(或不可避免的)解决方案。

您可以创建一个可具体化的子接口(interface),例如interface Specific extends MethodInterface<TestClass,String> {} ,将其用于代码生成,从编译器的角度来看没有未经检查的操作,但这不会改变代理根本不关心正确性的事实。

顺便说一句,如果你的目标接口(interface)是函数式接口(interface),你可以使用 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变体,它还有很多其他你可能会在编译器不告诉你的情况下做错的事情,这实际上并不重要。

关于java - 使用 MethodHandleProxies 的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44125869/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com