在我目前正在處理的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