问题 Java 9中的“模块本地”访问行为


作为核心 Jigsaw project是Java模块系统,能够限制对特定模块中特定程序元素(类,方法和字段)的访问是很好的。

当模块中的某些元素对于此模块基本上是公共的时,它可能会有所帮助,但不应在此模块外部访问。

所以我在谈论“package-local”之后的下一级访问,可以命名为“module-local”。

不过简单看一下 拼图规则 早期的规格并没有帮助我弄清楚这种功能。进一步来说 这个 Modifier 规范 不包含任何新元素。

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


6936
2017-08-11 13:30


起源

据我所知,模块默认是关闭的。你必须明确地输出东西,以便从外部获取它们。 openjdk.java.net/projects/jigsaw/quick-start 和 en.wikipedia.org/wiki/Java_Module_System 。我只是误解了你在这里问的问题吗? - Gimby
根据 模块系统的状态 只有导出的包在模块外部可用。看到 这张桌子 例如。 - aioobe
@Gimby你是对的:可以导出特定的包。但是方法和领域呢?是的,我认为你理解我的问题是对的。 - Andremoniy
我担心出口机制适用于包级别。引用上面链接的sotms:“因此,即使一个类型被声明为public,如果它的包未在其模块的声明中导出,那么它只能被该模块中的代码访问。跨模块边界引用的方法或字段是在这种意义上,如果其封闭类型是可访问的,并且如果成员本身的声明也允许访问,则可访问。“ - aioobe
我宁愿不写任何东西,因为它尚未发布,并且没有办法判断当前的功能是否会被发送。但你可以关注jigsaw-dev的讨论。例如,见 这个帖子。 - aioobe


答案:


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

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


10
2017-08-12 17:54



“但我们看到它没有引人注目的用例” - 不是在Java世界中,我敢肯定。你在其他地方看过吗?已经建立了一个非平凡的Swift,对我来说缺乏 internal 击败模块关于建模/设计的大部分潜在优势。我们的用例:a User 具有加密密钥的“资源”。而 User 是公共的,密钥应该是模块私有的,但不一定是包私有的。我猜你必须通过暴露接口来解决 - 但是你必须处理可能以你不期望的方式实现它们的用户。 - Raphael
子类化也会出现类似的问题。 final 防止子类 到处,所以我不能声明一个我可以在模块中子类化的类型,但可以肯定不会在它之外的子类。 (或者我可以吗?) - Raphael
“JVM中的访问控制的粒度比导出的包更精细” - 不是模块 粗糙 比包?我误解了吗? - Raphael
附录:缺乏模块专用访问级别意味着代码将根据可见性要求分发到包中,而不是建模考虑因素。我很难理解为什么那会是一件好事。 - Raphael


简答

当模块中的某些元素对于此模块基本上是公共的时,它可能会有所帮助,但不应在此模块外部访问。

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

答案很长

解释在该术语内 无障碍

Java编译器和虚拟机将一个模块中的公共类型视为一个模块 无障碍 仅在第一模块可由第二模块读取时,在上面定义的第一模块中,并且第一模块导出该包,则通过代码在一些其他模块中。 [...]

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

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

虽然编译器/ JVM认为类型可访问,但是有多种方式可以准确地导出包的方式和对象 没有其他机制适用。它的成员和Jigsaw之前一样容易接近。

这意味着无法在模块中看到可访问类型的成员(这将需要 public)但不在它之外(因为可访问类型的公共成员是可访问的)。

解决方法

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

是。 :)

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

现在,如果您的模块的API仅返回 Global-s但从不接受它们作为方法参数,你很高兴。只要确保你在内部总是使用 - 并且可能被投射到 - Local

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


5
2017-08-11 16:37