[英]How can I safely proxy a lambda when I don't know whether it's a lambda up-front?
我們有一些遺留的反射代理生成代碼,如果你把它看成黑盒子,基本上就是這樣的:
Object someObject = new Anything();
Object debugObject = ProxyUtils.wrapWithDebugLogging(someObject);
wrapWithDebugLogging接受任何對象並覆蓋它可以使用的任何方法(如果你擴展一個真正的類,最終方法顯然是不可修復的),攔截它以記錄有關該調用的消息,然后調用真實方法。
在內部,它使用cglib來完成工作,並在構造代理之前有一些保護邏輯,因為匿名類是最終的,但可以通過使用它們實現的超類或單個接口來處理:
Class<?> clazz = someObject.getClass();
Class<?> interfaces = clazz.getInterfaces();
// Anonymous classes are final so you can't extend them, but we know they only have one
// superclass or one interface.
if (clazz.isAnonymousClass()) {
clazz = interfaces.length > 0 ?
interfaces[0] : primaryType.getSuperclass();
}
Enhancer enhancer = new Enhancer();
if (clazz.isInterface()) {
interfaces.add(clazz);
} else {
enhancer.setSuperclass(primaryType);
}
enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
問題是Java 8的“lambda”類為isAnonymousClass()返回false。 但我們希望將它們與匿名類完全相同。
之前已經指出,沒有辦法確定一個類是“按設計”的lambda類。 但這似乎更像是反射API中缺少的東西,而且肯定不是Java第一次“忘記”向新API添加明顯的東西。
那么在API中沒有這個功能的情況下,有一種明智的方法來區分lambda和非lambda嗎? 我可以看到isSynthetic()返回true,但它也可以為各種其他東西返回true。
您不應該根據是否為lambda生成類的問題創建條件代碼。 畢竟,只有類的屬性很重要。
該類是final
所以你不能將它子類化。 即使它不是final
,子類也不可能,因為它只有private
構造函數。 它實現了一個interface
。 這些是該類的相關屬性。
在沒有任何lambda表達式的情況下遇到相同的場景並不是不現實的:
final class NotALambda implements Function<String,String> {
public static final Function<String,String> INSTANCE=new NotALambda();
private NotALambda() {}
public String apply(String t) {
return t.toLowerCase();
}
}
為什么要將此類與通過生成的類不同
Function<String,String> f=String::toLowerCase;
? 它具有相同的屬性和創建代理的相同障礙。 在評論中,你說你想根據方法是否被宣告為final
的問題而有所作為。 這樣做的意義就更小了,因為我可以在上面的例子中為方法添加一個final
修飾符,而不會改變任何東西,無論是創建代理時你將面臨的語義和困難。
我不認為這個限制(如果你想稱之為)應該是一個問題。
如果您正確地在適當的地方編程接口,那么使代理具有超類(除了Object
)變得不必要。 在可用時設置代理的超類(沒有private
構造函數,不是final
,等等)。
在所有情況下,您需要做的就是捕獲代理對象,目標,攔截其上的所有方法調用,執行日志記錄,然后將調用委托或路由到它。
public static Object wrapWithDebugLogging(Object target) {
... // prepare the enhancer as described above
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
logger.debug("Some useful logging message.");
return method.invoke(target, args);
}
});
return enhancer.create();
}
如果調用的方法是final
,你沒有試圖覆蓋它,你只是攔截它並不重要。
以下是快速實驗的結果。 foo
方法只輸出其參數的getClass().toString()
。 ( getClass().getName()
輸出相同的結果。)
foo (new Runnable() {
@Override
public void run() {
System.out.println("run");
}
});
輸出: Test32$1
foo (() -> System.out.println("run"));
輸出: Test32$$Lambda$1/640070680
不保證這是多么便攜。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.