Java 9 中的 requires 和 requires 传递语句有啥区别?

Posted

技术标签:

【中文标题】Java 9 中的 requires 和 requires 传递语句有啥区别?【英文标题】:What's the difference between requires and requires transitive statements in Java 9?Java 9 中的 requires 和 requires 传递语句有什么区别? 【发布时间】:2018-03-12 03:55:18 【问题描述】:

模块声明中的requiresrequires transitive模块语句有什么区别? 例如:

module foo 
    requires java.base;
    requires transitive java.compiler;

【问题讨论】:

【参考方案1】:

可读性回顾

如果模块barrequires模块drink,那么模块系统...

强制存在drink(称为可靠配置) 允许bar读取drink(称为readability) 允许bar中的代码访问drink中导出包中的公共类(称为accessibility

如果 bar requires transitive drink - drink 必须存在,可以读取和访问,则会发生完全相同的情况。事实上,对于 bardrinktransitive 关键字不会改变任何东西。

隐含的可读性

依赖于bar的模块是受transitive影响的模块:任何读取bar的模块也可以读取。换句话说,drink 的可读性是隐含的(这就是为什么它被称为implied readability)。结果是 customer 可以访问 drink 的类型。

所以如果bar requires transitive drinkcustomer requires bar,那么customer 可以读取drink,即使它没有明确依赖它。

用例

但是为什么呢?假设您有一个模块,其公共 API 接受或返回另一个模块的类型。假设 bar 模块公开返回 Drink 的实例,这是 drink 模块的接口:

// in module _bar_
public class Bar 

    // `Drink` comes from the module _drink_,
    // which _bar_ requires
    public Drink buyDrink()  /* ... */ 


在本例中,bar 使用常规的requires 表示drink。现在说,customer 依赖于bar,所以它的所有代码都可以调用Bar::buyDrink。但是当它发生时会发生什么?

模块系统抱怨 customer 不读取 drink,因此无法访问Drink。为了解决这个问题,customer 还必须依赖 drink。真是个苦差事!不能马上使用的酒吧有多没用?

出于这个原因,引入了隐含的可读性:使在其自己的公共 API 中使用另一个模块类型的模块立即可用不需要调用者查找并要求所有相关模块。 p>

因此,如果bar requires transitive drink客户 无需require drink - require bar 就可以开始购买饮料。应该的。

【讨论】:

我发现您的 bar-drink-customer 示例是最好的!感谢您在我的大脑中修复这些知识【参考方案2】:

两者之间的主要区别是依赖模块从一个到另一个的访问。

如果一个模块导出一个包,其中包含一个类型,其签名 引用第二个模块中的包,然后声明 第一个模块应该包括一个 requires transitive 依赖于 第二。这将确保依赖于 第一个模块将自动能够读取第二个模块,并且, 因此,访问该模块的导出包中的所有类型。


所以让我们说一下你的用例:-

module foo 
    requires java.base;
    requires transitive java.compiler;

~> 任何依赖于foo 模块的模块都会自动读取java.compiler 模块

~> 另一方面,要访问模块java.base,他们必须再次指定requires 子句。

module bar 
    requires foo; // java.compiler is available to read
    requires java.base; // still required

【讨论】:

【参考方案3】:

requires 描述了解决模块如何相互依赖的过程。

Quoting line

“requires”指令(与“传递”无关)表示 一个模块依赖于其他模块。 'transitive' 修饰符是让额外的模块也依赖 另一个模块。如果模块 M '需要传递 N',那么不仅 M 是否依赖于 N,但任何依赖于 M 的模块也依赖于 N. 这允许对 M 进行重构,使其部分或全部内容 可以移动到新的模块 N 而不会破坏具有 '需要 M' 指令。

简而言之:

requires - M 模块依赖于其他模块 N。

requires transitive - 附加模块隐式依赖于其他模块。例如:,如果 M 模块依赖于 N,而其他模块 P 依赖于 M。那么,它也隐式依赖于 N。

【讨论】:

【参考方案4】:

Nicolai 已经详细解释过了。我只是从 JDK 代码中给出一个具体的例子。考虑 jdk.scripting.nashorn 模块。该模块的module-info如下:

http://hg.openjdk.java.net/jdk9/dev/nashorn/file/17cc754c8936/src/jdk.scripting.nashorn/share/classes/module-info.java

它有这一行:

requires transitive java.scripting;

这是因为jdk.scripting.nashorn 模块在jdk.scripting.api.scripting 包中自己的API 接受/返回来自java.scripting 模块的javax.script 包的类型。所以 jdk.scripting.nashorn 告诉 JMPS 任何依赖于 jdk.scripting.nashorn 的模块也会自动依赖于 java.scripting 模块!

现在,同一个 jdk.scripting.nashorn 模块使用这一行:

    requires jdk.dynalink;

另一个模块jdk.dynalink。这是因为 jdk.scripting.nashorn 模块中none 的导出包(“API”)使用来自 jdk.dynalink 模块的类型。 jdk.scripting.nashorn 对 jdk.dynalink 的使用纯粹是一个实现细节。

【讨论】:

【参考方案5】:

Java 9 的 Java 语言规范用非常简单的术语解释了它。来自Module Dependences的部分:

requires 指令指定当前模块所依赖的模块的名称。

...

requires 关键字后面可以跟修饰符 transitive这会导致 requires 当前模块的任何模块隐式声明依赖于 requires transitive 指令指定的模块。

换句话说:

如果模块 X requires 模块 Y, 和模块 Y requires transitive 模块 Z, 然后模块 X 也(隐式地)requires 模块 Z。

【讨论】:

【参考方案6】:

可访问性这个词是模棱两可的:你可以访问对象而不访问它们的类型。如果一个对象的类型 T 位于未导出的包中,并且如果“导出”代码有一个返回 T 的方法......那么当调用此方法时,您将获得该 T 对象的句柄(并且您可以在其上调用与您的代码已知的任何类型有关的任何方法。

可读性也是模棱两可的:这并不意味着您的 ClassLoader 将始终无法加载(未导出的)T 类。

【讨论】:

【参考方案7】:

requires 和 requires 传递的区别上面已经解释过了,所以我只会添加 requires 传递的效果

如果 (模块 m 需要传递 n) && (模块 L 需要 m) 那么模块 L 不能在没有访问模块 n 的情况下被编译或执行

如果 (模块 m 需要传递 n) && (模块 L 需要 m 和 n) 那么模块 L 不需要显式声明 n 的需要

【讨论】:

以上是关于Java 9 中的 requires 和 requires 传递语句有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

requirements文件自动导入固定版本的包

列出 'require' 函数加载的所有变量

尝试设置.env文件时,“require is not defined”错误

require和require_once的区别

require.js

Google Adwords - CUSTOMER_ID_IS_REQUIRED 错误(Python)