在我目前正在处理的Java项目中,我动态加载类,然后使用反射API来查找并执行具有某些注释的类的方法。使用MethodHandleProxies的正确方法
执行实际执行的代码专门用于Java-8功能接口(出于兼容性原因),所以我需要一个中间阶段,其中使用反射发现的Method
实例被转换为适当的功能接口。我使用MethodHandleProxies
类来实现这一点。
再次出于兼容性原因,所讨论的功能接口是通用接口。这会在使用MethodHandleProxies.asInterfaceInstance
方法时导致“未检查转换”警告,因为该方法会返回“裸露”的界面。
以下是再现的主要步骤一个简单的例子:
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
非泛型将解决此特定问题,但意味着它不再适用于任意类型的方法引用(这对于代码的其他部分是可取的)。
例如,具有的TestClass
和MethodInterface
上述定义中,以下行编译:
MethodInterface<TestClass,String> iface = TestClass::testMethod;
然而,改变到的MethodInterface
打破了这种了以下定义:
@FunctionalInterface
public interface MethodInterface {
void call(Object inst, Object value);
}
分配TestClass::testMethod
到此接口的实例不会编译,因为参数的类型是错误的。
在我看来,我有三种选择:
- 简单地与警告居住。
- 添加一个
@SuppressWarnings
注释到作业。 - 想出另一种类型安全的方法。
我尽力确保没有通过我的代码生成的警告(以尽量减少错误机会),所以我并不热衷于选项1选项2个感觉它简直是“掩盖了裂缝”,但如果绝对必要的话可以接受。所以我最喜欢的选择是想出一个不同的方法。
是否有不同的方法是天生类型安全的?
显然'asInterfaceInstance'不支持泛型。你能解释为什么你需要一个通用的'MethodInterface'?在这种情况下,非泛型将会做到,并且很难想象为什么以及如何在代码的其余部分中使用通用泛型。 – CoronA
@CoronA:需要一个通用的'MethodInterface'来支持方法引用(在代码中的其他地方使用) - 请参阅更新后的问题。 – Mac