Java Modularity Part 3 – Directives

Java modularity

In the last post, we looked the concept of module declarations and module descriptors. We also used the two keywords exports and requires in a module declaration. This week we’ll look at some of the additional syntax that can be used with exports and requires.

If you missed the previous two posts, you can find them here:

In the last post we had the following module declaration:

module com.incusdata.office {
    requires com.incusdata.dao;
    exports com.incusdata.office.entity;
    exports com.incusdata.office.service;
}

Java Modularity: The requires Directive

The requires directive specifies that the module com.incusdata.office depends on the com.incusdata.dao module, i.e. there is a requirement for the com.incusdata.dao module to be accessible to the com.incusdata.office module.

requires module directive specifies that this module depends on another module — this relationship is called a module dependency.

The module com.incusdata.office has both a runtime and a compile-time dependency on com.incusdata.dao.

Java Modularity: The requires transitive Directive

We can specify implied readability by using requires transitive. We can specify a dependency on another module, and we can ensure that other modules reading our module also read that dependency, as follows:

requires transitive modulename;

For example, the java.sql module declaration includes the following directive:

requires transitive java.xml; 

In this case, any module that reads java.sql also implicitly reads java.xml. If a method from the java.sql module returns a type from the java.xml module, code in modules that need java.sql become dependent on java.xml. If the java.sql module didn’t use the requires transitive java.xml; directive, none of the dependent modules would compile unless they also explicitly read java.xml.

The requires transitive directive is commonly used for aggregator modules. These are modules with no packages and only transitive requirements. The java.se module is a good example. If we’re not interested in figuring out the fine-grained module dependencies, we can simply require java.se and then access all Java SE modules.

The java.se module is declared as follows:

module java.se {
    requires transitive java.compiler;
    requires transitive java.datatranser;
    requires transitive java.desktop;
    ...
    requires transitive java.sql;
    requires transitive java.sql.rowset;
    requires transitive java.xml;
    requires transitive java.xml.crypto;
}

Standard Java SE modules can only grant implied readability to other standard Java SE modules. They are not allowed to grant implied readability to any non-standard modules, even if they depend on these non-standard modules. This ensures that code that depends only on standard modules is portable across all Java SE implementations.

Java Modularity: The requires static Directive

We can create an optional dependency by using requires static. It is not very common to use this.

We use it when we write code in our module that references another module, but the users of our module will never use the other module directly. That module is required at compile time, but is optional at runtime, i.e. it is a compile-time only dependency. An example is if we want to use an annotation declared in a different module that must be processed at compile time.

Java Modularity: The exports Directive

An exports module directive specifies a particular package in the module whose public types should be accessible to other modules. These public types (classinterfaceenum) include their nested public and protected types as well.

Using the office example again, the com.incusdata.office module exports the two packages com.incusdata.office.entity and com.incusdata.office.service.

The exports directive specifies that the public members of com.incusdata.office.entity and com.incusdata.office.service packages will be accessible by the dependent modules. Only the listed package itself is exported. No sub-packages of the exported package are exported. Private members are not accessible.

Java Modularity: The exports … to Directive

We use exports to open up our public classes to the world. What if we don’t want everyone to be able to access our API? We can restrict which modules have access to our APIs using the exports ... to directive. It is similar to the exports directive, but we then list the modules that are allowed to import this package with their requires module directive.

Using an exports ... to directive allows us to specify which modules can access the exported package. This is known as a qualified export. We do this with a comma-separated list as follows:

module my.module {
    export com.my.package to com.other.module, com.another.module;
}

Listing Module Contents

Remember that we can list all the modules in our JDK by running the following from the command line:

java --list-modules

We can see the exports and requires directives in action by viewing the contents of an individual module. We do this by by running the following from the command line:

java --describe-module module.name

First view some small modules like java.prefsjava.compilerjava.datatransfer, or jdk.sql, as in:

java --describe-module jdk.compiler

After that, have a look at some of the larger modules, such as the main java.base module, or the java.desktop and java.xml modules.

When listing a module, we can see the packages that it exports, the packages it requires, the classes it uses, the internal packages it contains, etc. It should be starting to make a bit more sense now.

What’s Next?

In the next post, we’ll look at services in modules. These are specified with the uses and provides directives.

I’m always interested in your opinion, so please leave a comment. Your feedback helps me write tips that help you.

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.