2013-04-27 10 views
6

So weit wie JavaDoc-Status MethodHandles.lookup() Funktionen zurückgibt, die Zugriff auf die gleiche Methode/Funktionen/Konstruktor wie der Aufrufer dieser Funktion haben. Insbesondere, wenn der Aufrufer auf einige private Daten zugreifen kann, beispielsweise diese MethodHandles.Lookup-Funktion. Der folgende Code zeigt, dass dies falsch ist. Wo ich falsch liege?MethodHandle Lookup-Einrichtung

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); 
     } 

} 

Edit:

Dies ist, was ich bekommen:

java.lang.IllegalAccessException: Mitglied ist privat: MethodHandlerAccessTest $ NestedClass.foo() void, von MethodHandlerAccessTest unter java.lang.invoke.MemberName.makeAccessException (MemberName.java:507) um java.lang.invoke.MethodHandles $ L ookup.checkAccess (MethodHandles.java:1182) bei java.lang.invoke.MethodHandles $ Lookup.checkMethod (MethodHandles.java:1162) bei java.lang.invoke.MethodHandles $ Lookup.accessStatic (MethodHandles.java: 591) bei java.lang.invoke.MethodHandles $ Lookup.findStatic (MethodHandles.java:587) bei MethodHandlerAccessTest.testPrivateAccess (MethodHandlerAccessTest.java:19) bei sun.reflect.NativeMethodAccessorImpl.invoke0 (Mutter Methode) bei sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:57) bei sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke (Methode.java:601) um org.junit.runners.model.FrameworkMethod $ 1.runReflectiveCall (FrameworkMethod.java:47) um org.junit.internal.runners. model.ReflectiveCallable.run (ReflectiveCallable.java:12) bei org.junit.runners.model.FrameworkMethod.invokeExplosively (FrameworkMethod.java:44) bei org.junit.internal.runners.statements.InvokeMethod.evaluate (InvokeMethod.java:17) bei org.junit.runners.ParentRunner.runLeaf (ParentRunner.java:271) bei org.junit.runners.BlockJUnit4ClassRunner.runChild (BlockJUnit4ClassRunner.java:70) bei org.junit.runners .BlockJUnit4ClassRunner.runChild (BlockJUnit4ClassRunner.java:50) bei org.junit.runners.ParentRunner $ 3.run (ParentRunner.java:238) um ​​ org.junit.runners.ParentRunner $ 1.schedule (ParentRunner.java:63) um org.junit.runners.ParentRunner.runChildren (ParentRunner.java:236) um org.junit.runners.ParentRunner.access $ 000 (ParentRunner.java:53) um org.junit.runners.ParentRunner $ 2.evaluate (ParentRunner.java:229) um org.junit. runners.ParentRunner.run (ParentRunner.java:309) bei org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run (JUnit4TestReference.java:50) bei org.eclipse.jdt.internal.junit.runner .TestExecution.run (TestExecution.java:38) um org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests (Remo teTestRunner.java:467) bei org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests (RemoteTestRunner.java:683) bei org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run (RemoteTestRunner.java:390) unter org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main (RemoteTestRunner.java: 197)

Antwort

7

Das Problem ist, dass Ihre Testmethode nicht wirklich Anruf NestedClass.foo(). Diese Zeile:

NestedClass.foo(); 

... ist eigentlich in einem Aufruf einer Syntheseverfahren umgewandelt, die in foo erzeugt wird, wie folgt aus:

NestedClass.access$000(); 

Wo access$000 wie folgt aussieht:

// Note package access 
static void access$000() { 
    foo(); 
} 

Sie können dies überprüfen, indem Sie javap -c verwenden, um den tatsächlichen Bytecode zu betrachten.

Auf der JVM-Ebene hat Ihre äußere Klasse keinen Zugriff auf foo(). Der Java-Compiler nur synthetisiert Zugriff darauf durch Erstellen access$000 und Aufruf von Ihrer äußeren Klasse, wenn der Quellcode foo() ruft.

Zum Zeitpunkt der Ausführung, die Reflexion Bibliotheken nicht das gleiche tun, daher Ihr Fehler.

+0

Vielen Dank für Ihre Erklärung. Ich habe anderswo von * synthetischen Feldern/Methoden * gehört, aber ich habe nie wirklich mit ihm gesprochen. Warum hat MethodHandle dieses Verhalten nicht simuliert? Aus dem JavaDoc der Methode lookup() 'Gibt ein Suchobjekt auf dem Aufrufer zurück, das auf alle Methodenhandles zugreifen kann, auf die der Aufrufer Zugriff hat, einschließlich direkte Methodenhandles für private Felder und Methoden. Dieses Nachschlageobjekt ist eine Funktion, die an vertrauenswürdige Agenten delegiert werden kann. Ich habe es erwartet. – alexsmail

+1

@alexsmail: Der Compiler kann die synthetische Methode wie gewünscht implementieren. Ich möchte nicht, dass die JRE versucht, dasselbe zu tun. Es ist wichtig zu unterscheiden, was die * Sprache * erlaubt und was der * Bytecode * erlaubt. Was die VM betrifft, rufen Sie nicht 'foo()' auf, und Sie haben keinen Zugriff darauf. Die Sprache ist in der Lage, Ihnen "spezielle" Rechte zu geben, indem Sie die zusätzliche Methode hinzufügen und diese aufrufen - aber das ist nur ein Sprachproblem. Der Aufrufer (die äußere Klasse) hat wirklich * keinen Zugriff auf 'foo()' aus der Perspektive der VM. –

Verwandte Themen