Java MethodHandle查找工具
就JavaDoc而言,Java MethodHandle查找工具,java,reflection,java-7,Java,Reflection,Java 7,就JavaDoc而言,MethodHandles.lookup()返回能够访问与此函数调用方相同的方法/函数/构造函数的工具。具体地说,如果调用方可以访问一些私有数据,那么这个MethodHandles.Lookup工具可以处理这些数据。下面的代码表明这是错误的。我哪里弄错了 public class MethodHandlerAccessTest { private static class NestedClass { private static
MethodHandles.lookup()
返回能够访问与此函数调用方相同的方法/函数/构造函数的工具。具体地说,如果调用方可以访问一些私有数据,那么这个MethodHandles.Lookup工具可以处理这些数据。下面的代码表明这是错误的。我哪里弄错了
public class MethodHandlerAccessTest {
private static class NestedClass {
private static void foo(){}
}
@Test
public void testPrivateAccess() throws Throwable {
NestedClass.foo(); //compiles and executes perfectly
MethodType type = MethodType.methodType(void.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findStatic(NestedClass.class, "foo", type);
}
}
编辑:
这就是我得到的:
java.lang.IllegalacessException:成员是私有的:
MethodHandlerAccessTest$NestedClass.foo()无效,来自
MethodHandlerAccessTest在
java.lang.invoke.MemberName.makeAccessException(MemberName.java:507)
在
java.lang.invoke.MethodHandles$Lookup.checkAccess(MethodHandles.java:1182)
在
java.lang.invoke.MethodHandles$Lookup.checkMethod(MethodHandles.java:1162)
在
java.lang.invoke.MethodHandles$Lookup.accessStatic(MethodHandles.java:591)
在
java.lang.invoke.MethodHandles$Lookup.findStatic(MethodHandles.java:587)
在
MethodHandlerAccessTest.testPrivateAccess(MethodHandlerAccessTest.java:19)
位于的sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)
invoke(NativeMethodAccessorImpl.java:57)
在
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
位于java.lang.reflect.Method.invoke(Method.java:601)
org.junit.runners.model.FrameworkMethod$1.runReflectVeCall(FrameworkMethod.java:47)
在
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
在
org.junit.runners.model.FrameworkMethod.invokeeexplosive(FrameworkMethod.java:44)
在
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
位于org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
在
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
位于org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)位于
org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)位于
org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)位于
org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)位于
org.junit.runners.ParentRunner.run(ParentRunner.java:309)位于
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
在
org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
在
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
在
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
在
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
在
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
问题是您的测试方法并没有真正调用
NestedClass.foo()
。这一行:
NestedClass.foo();
。。。实际上转换为对在foo
中生成的合成方法的调用,如下所示:
NestedClass.access$000();
// Note package access
static void access$000() {
foo();
}
其中access$000
如下所示:
NestedClass.access$000();
// Note package access
static void access$000() {
foo();
}
您可以通过使用javap-c
查看实际字节码来验证这一点
在JVM级别,您的外部类无权访问foo()
。Java编译器只是通过创建access$000
并在源代码调用foo()
时从外部类调用它来综合对它的访问
在执行时,反射库不会执行相同的操作,因此会出现错误。感谢您的解释。我在别处听说过合成领域/方法,但我从未真正遇到过。为什么MethodHandle没有模拟这种行为?从JavaDoc of lookup()方法
返回调用者的查找对象,该对象能够访问调用者有权访问的任何方法句柄,包括私有字段和方法的直接方法句柄。这个查找对象是一个可以委托给受信任代理的功能。
我希望这样做。@alexsmail:编译器可以实现它选择的合成方法。我不希望JRE尝试做同样的事情。区分语言允许您做什么和字节码允许您做什么很重要。就VM而言,您没有调用foo()
,您也无权这样做。通过添加额外的方法并调用它,该语言可以为您提供“特殊”的权限,但这只是语言问题。从VM的角度来看,调用方(外部类)实际上没有访问foo()
的权限。虽然有点晚,但有一个简单的解决方法。将(NestedClass.class)中的MethodHandles.lookup()替换为MethodHandles.lookup()。以这种方式在外部类和内部类之间更改上下文类时,会保留私有访问功能,因此之后,可以访问嵌套类的private
成员。除此之外,从Java11开始,不再需要这个access$…
东西,但它还需要使用JDK11目标编译代码。