dimanche 8 septembre 2019

Unable to Reflectively Instantiate a "protected" type of an "opened" package, from another module

Summary

I'm unable to reflectively instantiate a class across module boundaries. The class has package-level access, and I'm trying to instantiate it from not just outside the package, but from outside the module itself. According to my understanding, I'm allowed to do this, so long as the containing package has been opened by the containing module, and the instantiating module requires the containing module. All I get for my attempted instantiation is an IllegalAccessException

Details

My app is configured as a "Modular Java project" in my Netbeans 11, and is configured to run on the Java 12 platform.

In the app, I have two modules:

  • mod.world
  • mod.vehicles

mod.world is my "main" module -- it has the main() method, and it declares that it requires the other module mod.vehicles

mod.vehicles defines a "protected" class (a class with package-level access) called vehicles.cars.Sedan, in package vehicles.cars

mod.vehicles declares that it opens vehicles.cars

In the main() method, I attempt to instantiate the Sedan class, using:

var myConstructor = Class.forName("vehicles.cars.Sedan").
                                        getDeclaredConstructor();

myConstructor.newInstance();

What I Expect to See

I expect that the code successfully instantiates the Sedan class, and the println() inside the Sedan constructor gets executed.

What I Actually Get

But this invocation of newInstance() throws an IllegalAccessException:

Exception in thread "main" java.lang.IllegalAccessException: class world.MyMainClass (in module mod.world) cannot access a member of class vehicles.cars.Sedan (in module mod.vehicles) with modifiers "public"

My Questions

According to the Java Language Specification (section 7.7.2 Exported and Opened Packages):

The opens directive specifies the name of a package to be opened by the current module. For code in other modules, this grants access at run time, but not compile time, to the public and protected types in the package, and the public and protected members of those types. It also grants reflective access to all types in the package, and all their members, for code in other modules.

  1. My understanding of the above extract from the JLS is that I should be able to access the "protected" type Sedan, and its members, which includes the constructor. But why am I not able to use that constructor to instantiate the Sedan class reflectively?

  2. Which part of the Java Language Specification am I violating by attempting this instantiation?

  3. Is my understanding of the term reflective access wrong? Is this term defined formally somewhere by Java? Couldn't find a formal definition of reflective access anywhere in the JLS for Java 12.

Here's the complete code:


module-info.java for the mod.world module:

module mod.world {
    exports world;
    requires mod.vehicles;
}


MyMainClass.java, in package world of module mod.world:

package world;

import java.lang.reflect.InvocationTargetException;

public class MyMainClass {
    public static void main (String[] args) throws ClassNotFoundException,
                                                NoSuchMethodException,
                                                InstantiationException,
                                                IllegalAccessException,
                                                InvocationTargetException {

        // This statement succeeds.
        var myConstructor = Class.forName("vehicles.cars.Sedan").
                                                getDeclaredConstructor();

        // This statement throws IllegalAccessException
        myConstructor.newInstance();
    }
}


module-info.java for the mod.vehicles module":

module mod.vehicles {
    opens vehicles.cars;
}


Sedan.java in package vehicles.cars:

package vehicles.cars;

class Sedan {
    public Sedan () {
        System.out.println ("Inside constructor Sedan()");
    }
}


Other Observation

The instantiation goes through successfully if I change the Sedan class to a public one.





Aucun commentaire:

Enregistrer un commentaire