As an example, let’s consider an application made up of the following modules:

The application’s main module, com.example:app, depends on two others, com.example:foo and com.example:bar. They in turn depend on the Log4j API and another module, com.example:greeter. The latter is used in two different versions, though.

Let’s take a closer look at the Greeter class in these modules. Here is the version in com.example:greeter@1.0.0, as used by com.example:foo:

1 2 3 4 5 6 public class Greeter { public String greet ( String name , String from ) { return "Hello, " + name + " from " + from + " (Greeter 1.0.0)" ; } }

And this is how it looks in com.example:greeter@2.0.0, as used by com.example:bar:

1 2 3 4 5 6 7 8 9 10 11 public class Greeter { public String hello ( String name , String from ) { return "Hello, " + name + " from " + from + " (Greeter 2.0.0)" ; } public String goodBye ( String name , String from ) { return "Good bye, " + name + " from " + from + " (Greeter 2.0.0)" ; } }

The Greeter API has evolved in a backwards-incompatible way, i.e. it’s not possible for the foo and bar modules to use the same version.

With a "flat" module path (or classpath), there’s no way for dealing with this situation. You’d inevitably end up with a NoSuchMethodError , as either foo or bar would be linked at runtime against a version of the class different from the version it has been compiled against.

The lack of support for using multiple module versions when working with the --module-path option might be surprising at first, but it’s an explicit non-requirement of the module system to support multiple module versions or even deal with selecting matching module versions at all.

This means that the module descriptors of both foo and bar require the greeter module without any version information:

1 2 3 4 5 module com . example . foo { exports com . example . foo ; requires org . apache . logging . log4j ; requires com . example . greeter ; }