簡體   English   中英

當我不知道它是否是一個lambda up-front時,我怎樣才能安全地代理lambda?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM