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

hat*_*hat 6 java java-platform-module-system java-9 java-module java-11

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

javac -p /path/to/jars/ App.java
Run Code Online (Sandbox Code Playgroud)

然而,这还不够。尝试编译会导致很多类似的错误

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)
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,我们可以使用 --add-modules 添加 javafx.graphics:

javac -p /path/to/jars/ --add-modules javafx.graphics App.java
Run Code Online (Sandbox Code Playgroud)

但是,如果我们将 module-info.java(仅包含module ui {})添加到项目中,则没有问题。

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

Zhe*_*lov 6

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

这是JEP 261的引述,解释了这种差异:

当编译器编译未命名模块中的代码,或调用java启动器并将应用程序的主类从类路径加载到应用程序类加载器的未命名模块中时,则未命名模块的默认根模块集计算如下

java.se 模块是一个 root,如果它存在的话。如果它不存在,则升级模块路径上的每个 java.* 模块或导出至少一个包的系统模块中的每个 java.* 模块,没有限定,都是一个根。

升级模块路径上或系统模块中的每一个非java.*模块,只要导出至少一个包,不加限定,也是一个根。

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

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

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

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

否则,默认的根模块集取决于阶段:

在编译时,它通常是正在编译的模块集

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

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

javac --upgrade-module-path /path/to/jars/ App.java
Run Code Online (Sandbox Code Playgroud)

或者通过使用--add-modules

javac -p /path/to/jars/ --add-modules ...
Run Code Online (Sandbox Code Playgroud)

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

javac -cp /path/to/jars/ App.java
Run Code Online (Sandbox Code Playgroud)

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