关于对语言水平的辅助签名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链接,因为这涉及太多话题。 –