Reflection: how can one access public fields (members) in a superclass ?

Rony G. Flatscher Rony.Flatscher at wu.ac.at
Wed Jan 24 10:29:58 UTC 2018


As it does not make sense to supply my current "under construction" code, I created two Java
programs, one using reflection and one MethodHandles that behave like my current testbed. I added
those two Java programs "TestUseViaReflection.java" (compile and run with
"7a_module_compile_and_run_TestUseViaReflection.cmd") and
"TestUseViaReflectionAndMethodHandles.java" (compile and run with
"8a_module_compile_and_run_TestUseViaReflectionAndMethodHandles.cmd") to the zip archive and
replaced the zip-archive on Dropbox
(<https://www.dropbox.com/s/fti7camrb5hs7we/02-20180123-getPublicField.zip?dl=0>) with the updated one.

This is the "pure reflection" version, that I would prefer, "TestUseViaReflection.java":

    import java.lang.reflect.Field;

    public class TestUseViaReflection
    {
        public static void main (String args[]) {
            mtest3.Class03A o=new mtest3.Class03A();

            Class sc     =o.getClass().getSuperclass(); // get superclass mtest2.Class02A

            Field  field = null;
            try {
                field =sc.getDeclaredField("pubStaticFromClass02A");  // get static field
            }
            catch (Exception e)
            {
                System.err.println("exception thrown in sc.getDeclaredField(...): "+e);
                e.printStackTrace();
                System.exit(-1);
            }

            Object result=null;
            try {
                result=field.get(o);                  // get field's value
            }
            catch (Exception e)
            {
                System.err.println("exception thrown in field.get(...): "+e);
                e.printStackTrace();
                System.exit(-1);
            }
            System.out.println("o.pubStaticFromClass02A     : "+result );
        }
    }

 Running it yields:

    G:\xfer\java9modules\02-20180123-getPublicField>java  -cp "." --module-path out --add-modules
    mod_A,mod_B,mod_C TestUseViaReflection
    exception thrown in field.get(...): java.lang.IllegalAccessException: class TestUseViaReflection
    cannot access class mtest2.Class02A (in module mod_B)
     because module mod_B does not export mtest2 to unnamed module @4e04a765
    java.lang.IllegalAccessException: class TestUseViaReflection cannot access class mtest2.Class02A
    (in module mod_B) because module mod_B does not export mtest2 to unnamed module @4e04a765
            at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
            at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:589)
            at java.base/java.lang.reflect.Field.checkAccess(Field.java:1075)
            at java.base/java.lang.reflect.Field.get(Field.java:416)
            at TestUseViaReflection.main(TestUseViaReflection.java:23)

----------------

This is the version that tries to use MethodHandles instead,
"TestUseViaReflectionAndMethodHandles.java":

    import java.lang.reflect.Field;
    import java.lang.invoke.*;

    public class TestUseViaReflectionAndMethodHandles
    {
        public static MethodHandles.Lookup lookup=MethodHandles.lookup();

        public static void main (String args[]) {
            mtest3.Class03A o=new mtest3.Class03A();

            Class sc     =o.getClass().getSuperclass(); // get superclass mtest2.Class02A

            Field  field = null;
            try {
                field =sc.getDeclaredField("pubStaticFromClass02A");  // get static field
            }
            catch (Exception e)
            {
                System.err.println("--> exception thrown in sc.getDeclaredField(...): "+e);
                e.printStackTrace();
                System.exit(-1);
            }

            Object result=null;
            MethodHandle mh;
            try {
                mh=lookup.unreflectGetter(field);
                result=mh.invoke(o);
            }
            catch (Throwable t)
            {
                System.err.println("--> exception thrown in field.get(...): "+t);
                t.printStackTrace();
                System.exit(-1);
            }
            System.out.println("o.pubStaticFromClass02A     : "+result );
        }
    }

 Running it yields:

    G:\xfer\java9modules\02-20180123-getPublicField>java  -cp "." --module-path out --add-modules
    mod_A,mod_B,mod_C TestUseViaReflectionAndMethodHandles
    --> exception thrown in field.get(...): java.lang.IllegalAccessException: access to public
    member failed: mtest2.Class02A.pubStaticFromClass02A/java.lang.String/getStatic, from
    TestUseViaReflectionAndMethodHandles (unnamed module @4e04a765)
    java.lang.IllegalAccessException: access to public member failed:
    mtest2.Class02A.pubStaticFromClass02A/java.lang.String/getStatic, from
    TestUseViaReflectionAndMethodHandles (unnamed module @4e04a765)
            at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:914)
            at java.base/java.lang.invoke.MethodHandles$Lookup.checkAccess(MethodHandles.java:2193)
            at java.base/java.lang.invoke.MethodHandles$Lookup.checkField(MethodHandles.java:2143)
            at
    java.base/java.lang.invoke.MethodHandles$Lookup.getDirectFieldCommon(MethodHandles.java:2355)
            at
    java.base/java.lang.invoke.MethodHandles$Lookup.getDirectFieldNoSecurityManager(MethodHandles.java:2350)
            at java.base/java.lang.invoke.MethodHandles$Lookup.unreflectField(MethodHandles.java:1863)
            at java.base/java.lang.invoke.MethodHandles$Lookup.unreflectGetter(MethodHandles.java:1854)
            at TestUseViaReflectionAndMethodHandles.main(TestUseViaReflectionAndMethodHandles.java:27)

----------------

However the Java program "TestUse_mtest3_Class03A.java" compiles and runs successfully:

Compiling the modules and then using them in the following Java program (via the CLASSPATH) works,
here the source:

    TestUse_mtest3_Class03A.java

            public class TestUse_mtest3_Class03A
            {
                public static void main (String args[]) {
                    mtest3.Class03A o=new mtest3.Class03A();
                    System.out.println("o.pubStaticFromClass02A     : "+o.pubStaticFromClass02A );
                    System.out.println("o.pubFromClass02A           : "+o.pubFromClass02A     );
                    System.out.println("o: "+o+", o.getMyClassName(): "+o.getMyClassName());
                }
            }

Compiling the above program and running it yields:

    o.pubStaticFromClass02A     : static-mtest2.Class02A
    o.pubFromClass02A           : instance-mtest2.Class02A
    o: mtest3.Class03A at 5afa04c, o.getMyClassName(): via: this=[mtest3.Class03A at 5afa04c],
    getMyClassName()=[class-mtest1.Class01A]

----------------

Maybe I miss something obvious.

---rony



On 23.01.2018 19:17, Rony G. Flatscher wrote:
> Was asked off-line for a zip-archive for convenience, hence I created a zip archive and put it on
> Dropbox. Here the link to get the zip-archive:
> <https://www.dropbox.com/s/fti7camrb5hs7we/02-20180123-getPublicField.zip?dl=0>.  Just unzip, go
> into the <02-20180123-getPublicField> directory and run (on Windows) "1_compile.cmd", then either 
> "5a_module_compile_and_run_TestUse_mtest3_Class03A.cmd" or
> "5b_classpath_compile_and_run_TestUse_mtest3_Class03A.cmd". Adapting the scripts to Unix should be
> straight forward.
>
> Being formally an OpenJDK contributor there should be no legal problems.
>
> ---rony
>
>
> On 23.01.2018 18:32, Rony G. Flatscher wrote:
>> Oh, forgot the scripts to compile and run (these are under Windows):
>>
>> Assuming that the sources of "mod_A", "mod_B" and m"od_C" are located in "src", the compilation
>> result should be placed into "out" here the compile script:
>>
>>     rd out /s /q  && md out
>>
>>     @echo compiling module mod_A
>>     dir      src\mod_A\*java /s /b > mod_A_source_files.txt
>>     type mod_A_source_files.txt
>>     javac -d out\mod_A @mod_A_source_files.txt
>>
>>     @echo compiling module mod_B
>>     dir      src\mod_B\*java /s /b > mod_B_source_files.txt
>>     type mod_B_source_files.txt
>>     javac --module-path out -d out\mod_B @mod_B_source_files.txt
>>
>>     @echo compiling module mod_C
>>     dir      src\mod_C\*java /s /b > mod_C_source_files.txt
>>     type mod_C_source_files.txt
>>     javac --module-path out -d out\mod_C @mod_C_source_files.txt
>>
>> Assuming that "TestUse_mtest3_Class03A.java" is located in the directory that contains the "out"
>> subdirectory:
>>
>>     del TestUse_mtest3_Class03A.class
>>     javac -cp "." --module-path out --add-modules mod_A,mod_B,mod_C TestUse_mtest3_Class03A.java
>>
>>     java  -cp "." --module-path out --add-modules mod_A,mod_B,mod_C TestUse_mtest3_Class03A
>>
>> ---rony
>>
>>
>> On 23.01.2018 15:52, Rony G. Flatscher wrote:
>>> Given three modules (sources at the end) where
>>>
>>>   * "mod_A" exports its package "mtest1", to everyone
>>>   * "mod_B" requires "mod_A" and exports its package "mtest2" to "mod_C"
>>>   * "mod_C" requires "mod_B" and exports its package "mtest3" to everyone
>>>
>>> "mod_B"'s class "mtest2.Class02A" defines two public fields, one static ("pubStaticFromClass02A")
>>> and one an instance ("pubFromClass02") one.
>>>
>>> Compiling the modules and then using them in the following Java program (via the CLASSPATH) works,
>>> here the source:
>>>
>>>     TestUse_mtest3_Class03A.java
>>>
>>>             public class TestUse_mtest3_Class03A
>>>             {
>>>                 public static void main (String args[]) {
>>>                     mtest3.Class03A o=new mtest3.Class03A();
>>>                     System.out.println("o.pubStaticFromClass02A     : "+o.pubStaticFromClass02A );
>>>                     System.out.println("o.pubFromClass02A           : "+o.pubFromClass02A     );
>>>                     System.out.println("o: "+o+", o.getMyClassName(): "+o.getMyClassName());
>>>                 }
>>>             }
>>>
>>> Compiling the above program and running it yields:
>>>
>>>     o.pubStaticFromClass02A     : static-mtest2.Class02A
>>>     o.pubFromClass02A           : instance-mtest2.Class02A
>>>     o: mtest3.Class03A at 5afa04c, o.getMyClassName(): via: this=[mtest3.Class03A at 5afa04c],
>>>     getMyClassName()=[class-mtest1.Class01A]
>>>
>>> Here is a 1:1 transcription from the above Java program to Rexx which uses Java reflection to
>>> achieve the same:
>>>
>>>     test.rex
>>>
>>>             o=.bsf~new("mtest3.Class03A")          -- create Java object
>>>             say "o~pubStaticFromClass01A:" o~pubStaticFromClass02A
>>>             say "o~pubFromClass01A      :" o~pubFromClass02A
>>>             say "o:" o "o~getMyClassName:" o~getMyClassName
>>>
>>>             ::requires BSF.CLS   -- direct interpreter to load Java bridge
>>>
>>> Running the Rexx program yields the following reflection error:
>>>
>>>     // // -> -> RexxReflectJava9.processField(): EXCEPTION in GET-operation:
>>>     tmpField="pubStaticFromClass02A" exception: "java.lang.IllegalAccessException: class
>>>     org.rexxla.bsf.engines.rexx.RexxReflectJava9 cannot access class mtest2.Class02A (in module
>>>     mod_B) because module mod_B does not export mtest2  to unnamed module @51c8530f"
>>>
>>> The reflection code currently
>>>
>>>   * gets the type from the Java object ("mtest3.Class03A") and tests whether the package "mtest3" is
>>>     exported (it is),
>>>   * looks for all declaredFields and finds none, so it gets the superclass "mtest2.Class02A",
>>>   * looks for all declaredFields and locates the Field named "pubStaticFromClass02A" and invokes the
>>>     Field's get method, supplying the Java object (an instance of class mtest3.Class03A) which
>>>     causes an IlleagalAccessException.
>>>
>>> Although it is true that "mod_B" is not exported to the unnamed module it is still the case that
>>> "mod_C" is exported (and class "mtest3.Class03A" can be accessed), such that all public members in
>>> its superclasses should be accessible via reflection, even in the case that a public member resides
>>> in a module that is not exported to the reflector from the unnamed module?
>>>
>>> The reflective code would be able to assess that the supplied object is from an exported type and
>>> hence allow the get access in this case for reflected members in its superclasses, like it seems the
>>> Java compiler allows for.
>>>
>>> ---rony
>>>
>>> Here are the contents of the module directories in source:
>>>
>>>     ---------------------------------------------------------------------------
>>>
>>>     mod_A/module-info.java
>>>
>>>             module mod_A { exports mtest1; }
>>>
>>>     mod_A/mtest1/Class01A.java
>>>
>>>             package mtest1;
>>>
>>>             abstract public class Class01A
>>>             {
>>>                 protected static String myClassName = "class-mtest1.Class01A";
>>>             }
>>>
>>>     ---------------------------------------------------------------------------
>>>
>>>     mod_B/module-info.java
>>>
>>>             module mod_B {
>>>                 requires mod_A;
>>>                 exports mtest2 to mod_C;
>>>             }
>>>
>>>     mod_B/mtest2/Class02A.java
>>>
>>>             package mtest2;
>>>
>>>             public class Class02A extends mtest1.Class01A
>>>             {
>>>                 public static String pubStaticFromClass02A="static-mtest2.Class02A";
>>>                 public        String pubFromClass02A      ="instance-mtest2.Class02A";
>>>
>>>                 public String getMyClassName()
>>>                 {
>>>                     return "via: this=["+this+"], getMyClassName()=["+myClassName+"]";
>>>                 }
>>>             }
>>>
>>>     ---------------------------------------------------------------------------
>>>
>>>     mod_C/module-info.java
>>>
>>>             module mod_B {
>>>                 requires mod_A;
>>>                 exports mtest2 to mod_C;
>>>             }
>>>
>>>     mod_C/mtest3/Class03A.java
>>>
>>>             package mtest3;
>>>
>>>             public class Class03A extends mtest2.Class02A
>>>             {
>>>             }
>>>
>>>     --------------------------------------------------------------------------



More information about the jigsaw-dev mailing list