【问题标题】:"Module local" access behaviour in Java 9Java 9 中的“模块本地”访问行为
【发布时间】:2016-12-18 06:40:18
【问题描述】:

Jigsaw 项目的核心是 Java 模块系统,如果能够限制对特定模块内特定程序元素(类、方法和字段)的访问,那就太好了。

当模块中有一些元素对于这个模块本质上是公共的,但在这个模块之外不应该被访问时,它会很有帮助。

所以我说的是“package-local”之后的下一个访问级别,可以命名为“module-local”。

然而,对Jigsaw rules 和早期规格的简要了解并没有帮助我找到这种功能。更具体地说,this Modifier specification 不包含任何新元素。

那么在未来的 Java 9 中还有其他可能吗?

【问题讨论】:

  • 据我了解,模块默认是关闭的。你必须明确地导出东西以使它们从外部需要。 openjdk.java.net/projects/jigsaw/quick-starten.wikipedia.org/wiki/Java_Module_System 。我只是误解了你在这里问的问题吗?
  • 根据The State of the Module System,只有导出的包在模块外可用。例如,参见this table
  • @Gimby 你说的很对:可以导出特定的包。但是方法和字段呢?是的,我认为您非常正确地理解了我的问题。
  • 恐怕导出机制在包级别起作用。引用上面链接的 sotms:“因此,即使一个类型被声明为 public,如果它的包没有在其模块的声明中导出,那么它只能被该模块中的代码访问。跨模块边界引用的方法或字段是如果它的封闭类型在这个意义上是可访问的,并且如果成员本身的声明也允许访问,则可以访问。"
  • 我宁愿不写任何东西,因为它还没有发布,也没有办法判断当前的功能是否会被发布。但是你可以关注 jigsaw-dev 上的讨论。参见例如this thread

标签: java access-modifiers java-9 java-platform-module-system


【解决方案1】:

public 元素(,一个类、接口、方法或字段) 非导出包实际上是“本地模块”。这将是 模块中的所有其他代码都可以访问,但不能从模块外部访问 模块。

无法在导出的包中声明模块本地元素。 导出包的public 元素可从外部访问 模块,package-private 元素仍然是 package-private,并且有 这两种模式之间没有元素级访问模式。我们可以定义一个 新的此类模式,但我们很少看到有说服力的用例,而且, 此外,在JVM中以粒度实现模块化访问控制 比导出的包更精细会带来显着的性能 费用。

【讨论】:

  • “但我们很少看到有说服力的用例”——我敢肯定,在 Java 世界中不会。你在别处看过吗?在构建了一个不平凡的 Swift 之后,对我来说,缺少 internal 会破坏模块在建模/设计方面的大部分潜在优势。我们的用例:具有加密密钥的User“资源”。虽然User 是公开的,但密钥应该是模块私有的,但不一定是包私有的。我想您只能通过公开接口来解决问题 - 但是您必须与可能以您不期望的方式实现它们的用户打交道。
  • 子类化也会出现类似的问题。 final 防止子类无处不在,所以我不能声明可以在模块内子类化的类型,但可以肯定不会在它之外被子类化。 (或者我可以吗?)
  • “JVM 中的访问控制比导出包的粒度更细” -- 模块不是coarser 比包吗?我是不是误会了?
  • 附录:缺少模块私有访问级别意味着代码将根据可见性要求而不是建模考虑分发到包中。我很难理解为什么这是一件好事。
【解决方案2】:

简答

当模块中有一些元素对于这个模块本质上是公共的,但在这个模块之外不应该被访问时,它会很有帮助。

这是不可能的。 (仅使用模块系统 - 有一种解决方法。)

长答案

解释在Accessibility这个词内:

Java 编译器和虚拟机认为只有当第一个模块可由第二个模块读取时,一个模块中的包中的公共类型才能可被其他模块中的代码访问,在某种意义上上面定义的,第一个模块导出该包。 [...]

以这种方式无法访问的跨模块边界引用的类型无法使用,就像私有方法或字段无法使用一样:任何尝试使用它都会导致编译器报告错误,或者@ 987654322@ 由 Java 虚拟机抛出,或 IllegalAccessException 由反射运行时 API 抛出。 [...]

如果其封闭类型在这个意义上是可访问的,并且成员本身的声明也允许访问,则跨模块边界引用的方法或字段是可访问的。

虽然有不同的方式可以准确地导出包的方式和对象,但一旦编译器/JVM 认为类型可访问没有额外的机制适用。它的成员与 Jigsaw 之前一样易于访问。

这意味着没有办法让可访问类型的成员在模块内可见(这需要public),但在模块外部不可见(因为可访问类型的公共成员是可访问的)。

解决方法

那么在未来的 Java 9 中还有其他可能吗?

是的。 :)

您可以在导出的包中拥有一个公共接口Global,该接口定义了您想要导出到世界的方法。然后有一个接口或一个类Local 扩展Global 并添加所有你想要的成员。关键是Local 必须在导出的包中!

现在,如果您的模块的 API 只返回 Global-s 但从不接受它们作为方法参数,那么您就可以开始了。只需确保您在内部始终使用 - 并且可能转换为 - Local

如果您也接受Global-s,您必须清楚地证明这些只能是您的 API 返回的实例(即不允许用户创建自己的实现)。这听起来可能令人望而却步,但如果您仔细考虑最初的请求,它会具有相同的特征。

【讨论】:

  • 我面临同样的问题。正如您所提到的,如果内部 API 模块中的方法只接受 Global 对象,我需要在这里和那里将它们转换为 Local。我不喜欢这样,因为它不干净并且可能需要检查类型安全(用户可以从Global 创建一个新类,它不实现Local)。如果我将内部模块中的所有类/接口放在同一个 pkg 中并对某些成员使用 pkg-visibility,我可以接受 Local 作为参数,从而解决问题。但是,我认为将所有内容放在同一个 pkg 中是很可怕的。有任何想法吗?谢谢
猜你喜欢
  • 2018-04-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
相关资源
最近更新 更多