簡體   English   中英

我想打印 hi GrandFather;但它似乎打印 hifather

[英]I want to print hi GrandFather;but it seems to print hi father

我想打印hi GrandFather

但它似乎打印喜父親。 我不明白如何findSpecialfindVirtual之間的使用

我想要有人可以幫助我。 謝謝

class GrandFather{
    void thinking(){
        System.out.println("hi GrandFather");

    }
}
class Father extends GrandFather{
    void thinking(){
        System.out.println("hi Father");
    }
}
class Son extends Father{
    void thinking(){
            MethodType mt=MethodType.methodType(void.class);

            //MethodHandle mh=MethodHandles.lookup().findVirtual(GrandFather.class,"thinking",mt).bindTo(this);
            MethodHandle mh;
                mh = MethodHandles.lookup().findSpecial(GrandFather.class,"thinking",mt,getClass());
                mh.invoke(this);
    }
}
public static void main(String[] args){
    (new MethodHandleTest().new Son()).thinking();
}

控制台屏幕截圖顯示打印了“嗨父親”

findSpecial的最后一個參數指定調用的上下文類。 您指定了getClass() ,這將導致Son.class 來自Sonsuper調用只能在Father結束,就像源代碼中的普通super.thinking()調用一樣。

您需要將Father.class指定為上下文類,因為允許FatherGrandFather執行super調用。 如果您在沒有進一步更改的情況下執行此操作,您將收到類似java.lang.IllegalAccessException: no private access for invokespecial…的異常。 您必須更改您的lookup()對象的上下文,才能訪問Father private功能:

MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = MethodHandles.lookup().in(Father.class)
    .findSpecial(GrandFather.class, "thinking", mt, Father.class);
mh.invoke(this);

這是有效的,因為SonFather是同一個頂級類的內部類,因此允許訪問彼此的私有成員。 如果它們不是同一頂級類中的類,則in(…)將更改上下文類但清除私有訪問權限。 在這種情況下,只有 Java 9 及更新版本有官方解決方案:

MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = MethodHandles.privateLookupIn(Father.class, MethodHandles.lookup())
    .findSpecial(GrandFather.class, "thinking", mt, Father.class);
mh.invoke(this);

FatherSon在同一個模塊中或Father的模塊已將Father的包打開到Son的模塊進行反射時,此方法有效。

Holger 像往常一樣是正確的。 我實際上花了一段時間才明白發生了什么,請將其視為對另一個非常好的答案的修正。 為了理解這一點,我不得不做 3 個不同的例子。

public class FindSpecialFailure {

    public static void main(String[] args) {
        try {
            MethodType mt = MethodType.methodType(void.class);
            Lookup l = MethodHandles.lookup();
            MethodHandle mh = l.findSpecial(Son.class, "go", mt, Son.class);
            mh.invoke(new Son());
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    static class Parent {
        void go() {
            System.out.println("parent");
        }
    }

    static class Son extends Parent {
        void go() {
            System.out.println("son");
        }
    }
}

如果你運行它,它會失敗並顯示java.lang.IllegalAccessException: no private access for invokespecial... 文檔給出了為什么會發生這種情況的正確說明:

通常,可以為方法 M 查找方法句柄的條件並不比查找類可以編譯、驗證和解析對 M 的調用的條件更具限制性

相同的文檔也解釋了這一點:

強制執行這些限制的調用者類稱為查找類

在我們的例子中, lookup classFindSpecialFailure ,因此,這將用於判斷來自Son.class方法go Son.class可以被編譯、驗證和解析

你可以想得更簡單一點。 你能(理論上)在FindSpecialFailure::main創建一個invokeSpecial字節碼指令並調用它嗎? 再次,理論上。 你可以在那里創建它:

invokeSpecial Son.go:()V

你能調用它嗎? 嗯,沒有 具體來說,以我的理解,這條規則將被打破:

如果符號引用命名一個類(不是接口),則該類是當前類的超類。

顯然Son不是FindSpecialFailure的超類。

為了證明我的觀點,您可以將上面的代碼更改為(第二個示例):

// example 1
public class FindSpecialInterface {

    public static void main(String[] args) {
        try {
            MethodType mt = MethodType.methodType(void.class);
            Lookup l = MethodHandles.lookup();
            // <---- Parent.class
            MethodHandle mh = l.findSpecial(Parent.class, "go", mt, Son.class);
            mh.invoke(new Son());
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    // <---- this is now an interface
    interface Parent {
        default void go() {
            System.out.println("parent");
        }
    }

    static class Son implements Parent {
        public void go() {
            System.out.println("son");
        }
    }
}

這一次一切都會正常工作,因為( 來自相同的規范):

否則,如果 C 是一個接口並且類 Object 包含一個與解析方法具有相同名稱和描述符的公共實例方法的聲明,那么它就是要調用的方法。

我如何修復第一個例子? ( FindSpecialFailure ) 您需要添加一個有趣的選項:

public static void main(String[] args) {
    try {
        MethodType mt = MethodType.methodType(void.class);
        // <--- Notice the .in(Son.class) --> 
        Lookup l = MethodHandles.lookup().in(Son.class);
        MethodHandle mh = l.findSpecial(Son.class, "go", mt, Son.class);
        mh.invoke(new Son());
    } catch (Throwable t) {
        t.printStackTrace();
    }
} 

如果您轉到相同的文檔,您會發現:

在某些情況下,嵌套類之間的訪問是由 Java 編譯器通過創建包裝器方法來訪問同一頂級聲明中另一個類的私有方法來獲得的。 例如,嵌套類 CD 可以訪問其他相關類(如 C、CDE 或 CB)中的私有成員,但 Java 編譯器可能需要在這些相關類中生成包裝方法。 在這種情況下,CE 上的 Lookup 對象將無法訪問這些私有成員。 此限制的一種解決方法是 Lookup.in 方法,它可以將 CE 上的查找轉換為任何其他類上的查找,無需特殊提升特權。

第三個例子不知何故開始看起來更像你的例子:

public class FinSpecialMoveIntoSon {

    public static void main(String[] args) {
        new Son().invokeMe();
    }

    static class Parent {
        public void go() {
            System.out.println("parent");
        }
    }

    static class Son extends Parent {

        void invokeMe() {
            try {
                MethodType mt = MethodType.methodType(void.class);
                Lookup l = MethodHandles.lookup();
                MethodHandle mh = l.findSpecial(Son.class, "go", mt, Son.class);
                mh.invoke(new Son());
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }

        public void go() {
            System.out.println("son");
        }
    }
}

這個問題的重點是findSpecial的文檔說明了第一個參數:

refc 訪問方法的類或接口。

這就是為什么它會打印Son而不是Parent


有了這個,你的例子就更容易理解了:

static class Son extends Father {

    void thinking() {
        try {
            MethodType mt = MethodType.methodType(void.class);
            MethodHandle mh = MethodHandles.lookup().findSpecial(GrandFather.class, "thinking", mt, getClass());
            mh.invoke(this);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

查找類Son.class ,方法解析和refc從那里訪問方法的類或接口)是GranFather 所以決議確實GrandFather::thinking開始,但由於你不能在java中調用super.super方法,所以它被“降級”為Father::thinking

我在這里只能建議使用.in來解決這個問題,我不知道privateLookupIn

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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