關於對語言水平的輔助簽名static java.lang.Void access$000();
的方法,有直接的聲明JLS §6.6.1, Determining Accessibility:
...
- 否則,該成員或構造函數聲明爲
private
,並且只有當它出現在包含聲明的頂層類(§7.6)成員或構造函數。
由於所有嵌套類和lambda表達式駐留在同一個「頂層階級的身體」內,這已經足以說明該訪問的有效性。
但是lambda表達式是根本不同的內部類反正:
JLS §15.27.2, Lambda Body:
與出現在匿名類聲明的代碼,名稱的含義和this
和super
關鍵字出現在拉姆達體,以及引用聲明的可訪問性,與周圍環境中的相同(除了lambda參數引入新名稱)。
這使得明顯,lambda表達式可以訪問它的類,這是在它被定義的類,而不是功能接口private
成員。 lambda表達式沒有實現功能接口,也不是從它繼承成員。它與目標類型是類型兼容的,當運行時調用函數方法時,會有一個函數接口的實例執行lambda表達式的主體。
該實例的生成方式是故意未指定的。作爲關於技術細節的評論,在參考實現中生成的類可以訪問private
另一類的方法,這是必要的,因爲爲lambda表達式生成的合成方法也將是private
。這可以通過將MethodInvoker.invoke(Test::method);
添加到您的測試用例中來說明。此方法參考允許在類Test
內直接調用method
,而無需任何合成方法。
雖然反射是不同的事情。它甚至不出現在語言規範中。這是一個圖書館功能。當涉及到內部類的可訪問性時,這個庫已經存在已知的問題。這些問題與內部類自身一樣古老(自Java 1.1以來)。有JDK-8010319
, JVM support for Java access rules in nested classes當前狀態爲目標的Java 10 ...
如果你真的需要內部類中反射訪問,你可以使用java.lang.invoke
包:
public class Test {
private static Void method() {
System.out.println("OK");
return null;
}
public static void main(String[] args) throws Exception {
// captures the context including accessibility,
// stored in a local variable, thus only available to inner classes of this method
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle method = lookup.findStatic(Test.class, "method",
MethodType.methodType(Void.class));
// TEST 2
MethodInvoker.invoke(new Callable() {
public Object call() throws Exception {
// invoking a method handle performs no access checks
try { return (Void)method.invokeExact(); }
catch(Exception|Error e) { throw e; }
catch(Throwable t) { throw new AssertionError(t); }
}
});
// TEST 3
MethodInvoker.invoke(new Callable() {
// since lookup captured the access context, we can search for Test's
// private members even from within the inner class
MethodHandle method = lookup.findStatic(Test.class, "method",
MethodType.methodType(Void.class));
public Object call() throws Exception {
// again, invoking a method handle performs no access checks
try { return (Void)method.invokeExact(); }
catch(Exception|Error e) { throw e; }
catch(Throwable t) { throw new AssertionError(t); }
}
});
}
}
當然,由於MethodHandles.Lookup
對象和MethodHandle
包含無需進一步檢查即可訪問其創建者的private
成員,必須注意不要將其交給意外的人。但爲此,您可以在現有的語言級別可訪問性上下定論。如果在private
字段中存儲查找對象或句柄,則只有同一頂級類中的代碼才能訪問它,如果使用本地變量,則只有同一本地作用域內的類才能訪問它。
因爲只有java.lang.reflect.Method
事項直接調用者,另一種解決方案是使用蹦牀:
public class Test {
private static Void method() {
System.out.println("OK");
return null;
}
public static void main(String[] args) throws Exception {
Method method = Test.class.getDeclaredMethod("method");
// TEST 3
MethodInvoker.invoke(new Callable() {
@Override
public Object call() throws Exception {
return invoke(method, null); // works
}
});
}
private static Object invoke(Method m, Object obj, Object... arg)
throws ReflectiveOperationException {
return m.invoke(obj, arg);
}
}
好,但請,我強調的是,我根據JLS – Andremoniy
但你期待字符串解釋在你的(可能的)假設中是正確的,這個問題是由我們以前的問題的答案引起的:) – Andremoniy
對不起,搞砸了數字。現在應該修復。對不起,但我不會提供所有內容的jls鏈接,因爲這涉及太多話題。 –