What is modularity in Java? To some, it’s a principle for development: programming to interfaces and hiding the details of implementations. This is the school of encapsulation. To others, it’s about leaning hard on class loaders to provide dynamic execution environments. This is the school of isolation. To still others, it’s about artifacts, repositories, and tooling. This is the school of configuration. Individually, these perspectives are valid, but they feel like pieces of a larger story that’s not quite clear. If a developer knows that some portion of their code is for internal use only, why can’t they hide a package as easily as hiding a class or a field? If code can be compiled and run only in the presence of its dependencies, why don’t those dependencies flow smoothly from compilation to packaging to installation to execution? If tools work only when presented with pristine self-describing artifacts, how can anyone reuse older libraries that are just plain JAR files?
Java 9 offers a coherent story for modularity by introducing modules as a first-class feature of the Java platform. A module is a set of packages designed for reuse. This simple concept has a surprisingly powerful impact on how code is developed, deployed, and run. The longstanding mechanisms for promoting and controlling reuse in Java—interfaces, access control, JAR files, class loaders, dynamic linking—all work better when packages are placed into modules.
First, modules clarify the structure of a program in a way that other mechanisms cannot. Many developers will be surprised that their code is not as well structured as they thought. For example, a codebase spread across multiple JAR files has a good chance of cycles between classes in different JAR files, but cycles between classes in different modules are forbidden. One of the motivations for investing in the modularization of a codebase is the knowledge that, once complete, there won’t be any backsliding into the ball of mud that cyclic dependencies allow. Developing with modules also leads to programming with services, which reduce coupling and increase abstraction even further.
Second, modules engender a sense of responsibility for code in a way that other mechanisms cannot. A developer who exports packages from a module is making a commitment to a stable API, and even the name of the module itself is part of the API. A developer who bundles too much functionality into a single module will cause that module to drag in a large number of dependencies that are irrelevant for any single task; anyone who reuses the module will realize its sprawling nature even if its internals are hidden. Developing with modules encourages every developer to think about the stability and cohesiveness of their code.
Most people are familiar with the tablecloth trick, where the cloth is whipped off the table without upsetting the plates and cups. For those of us who worked on Java 9, designing a module system that could slide into the Java Virtual Machine underneath the millions of classes developed since the 1990s felt rather like performing the trick in reverse. It turned out that modularizing the JDK caused the trick to fail, because some well-known libraries derived their power from trying to ignore the very encapsulation that the module system applied to the JDK’s modules. This tension in the design of Java 9 had no easy academic answers. In the end, a strong cycle of feedback from the community led to the module system offering developers a variety of levers and dials, so that modularized platform code can enjoy truly strong encapsulation while modularized application code can enjoy “strong enough” encapsulation. Over time, we think the bold choices made in modularizing the JDK will make all code more reliable.
A module system works best when it works for everyone. The more developers who create modules today, the more developers who will create modules tomorrow. But what about developers who have not created their modules yet? It is no exaggeration to say that Java 9 is just as concerned about the code that is not in modules as about the code that is. The only developer who should modularize a codebase is its author, so until that happens, the module system has to provide a way for code in modules to reach out to code not in modules. This led to the design of automatic modules which are so well explained in this book.
Sander and Paul are expert practitioners of Java and trusted guides to the Java 9 ecosystem. They were on the front lines of Java 9’s development, and in the vanguard of efforts to migrate popular open source libraries. Java 9 Modularity is the handbook for everyone interested in the core principles and best practices of modularity in Java: application developers looking to create maintainable components; library developers looking for advice on migration and reflection; and framework developers wishing to exploit the module system’s advanced features. I hope this book will help you to create Java programs whose structure lets them stand the test of time.