为什么模块路径上的模块需要 --add-modules?

Why is --add-modules necessary for modules which are on the module path?

例如:由于 JavaFx 从 JDK 中删除,JavaFx SDK 现在作为一组模块化 jar 分发。要编译JavaFx应用程序,当然你必须把它们放在模块路径上:

javac -p /path/to/jars/ App.java

然而,这还不够。尝试编译会出现很多类似

的错误
sample/App.java:3: error: package javafx.application is not visible
import javafx.application.Application;
             ^
  (package javafx.application is declared in module javafx.graphics, which is not in the module graph)

要解决这个问题,我们可以使用 --add-modules:

添加 javafx.graphics
javac -p /path/to/jars/ --add-modules javafx.graphics App.java

不过,如果我们向项目添加一个模块-info.java(仅包含 module ui {}),我们就没有问题。

为什么模块路径上的模块对命名模块可见但对未命名模块不可见?

+1 一个很好的问题。这里的问题是,当您编译命名和未命名模块时,它们的默认根模块集的计算方式非常不同。

这是引自 JEP 261 的一句话,它解释了这种差异:

When the compiler compiles code in the unnamed module, or the java launcher is invoked and the main class of the application is loaded from the class path into the unnamed module of the application class loader, then the default set of root modules for the unnamed module is computed as follows:

The java.se module is a root, if it exists. If it does not exist then every java.* module on the upgrade module path or among the system modules that exports at least one package, without qualification, is a root.

Every non-java.* module on the upgrade module path or among the system modules that exports at least one package, without qualification, is also a root.

这可能看起来有点复杂,所以我将文本中最重要的部分用粗体显示。另外,让我们一步一步来:

  • 您没有module-info.java,所以您的模块是未命名的模块。
  • java.se 存在,所以进入根集。
  • 您的升级模块路径为空(因为您只指定了 -p 而不是 --upgrade-module-path)。
  • 导出至少一个包的系统模块也进入集合。

因此根集只有java.se和一些系统模块。并且没有 JavaFX 模块进入集合!

现在,当您使用 module-info.java 编译时会发生什么?使用不同的规则计算根集:

Otherwise, the default set of root modules depends upon the phase:

At compile time it is usually the set of modules being compiled

由于根模块是需要 JavaFX 模块的模块,因此它们进入了模块图。

那么,如何解决这个问题呢?您可以通过将 JavaFX 模块放在升级模块路径上来实现:

javac --upgrade-module-path /path/to/jars/ App.java

或使用 --add-modules:

javac -p /path/to/jars/ --add-modules ...

或者使用普通的旧类路径:

javac -cp /path/to/jars/ App.java

所有三个选项都应该有效。让我知道第一个选项是否真的有效,因为我没有尝试过。