英文:
How to make Java class invisible outside its module for other non-modular projects?
问题
最近我发现,如果模块内部的包含有公共接口或类的包,它们会自动被导出,从而在未模块化的接收方代码中无意中可见。
项目的结构如下
parent
sub-donor (java模块)
my/project/first
Cowboy.java
module-info.java
sub-recipient (没有module-info)
my/project/somewhere
Main.java
module-info.java
module my.donor {
// 这里没有导出任何内容
}
Cowboy.java
public class Cowboy {
public String say() { return "eee-ha"; }
}
Main.java
import my.project.first.Cowboy;
public class Main {
public static void main(String[] args) {
Cowboy g = new Cowboy();
System.out.println(g.say());
}
}
my.project.first
包中的类应该只在 my.donor
模块中使用,并且在模块外部应该是不可见的。
令人意外的是,它在模块外部是可见的,就好像有一条 exports my.project.first;
的语句一样,因此类 Main
可以被编译。
限制可见性的方法是将 sub-recipient
设为一个Java模块,添加 module-info.java
,然后sub-donor
内部的所有内容都会按预期隐藏起来。当然,对于任何其他非模块化的项目,my.donor
模块中的每个公共类仍然是可见的。
请在GitHub上查看一个最小的工作示例。
我想知道这是一个错误还是一个有意的设计。
这种方法的目的是什么?
英文:
Recently I've found out that if a package inside a module contains public interfaces or classes they are being exported automatically and hence unintentionally visible outside the module in case when recipient code is not modularized.
Structure of the project is as follows
parent
sub-donor (java-module)
my/project/first
Cowboy.java
module-info.java
sub-recipient (has no module-info)
my/project/somewhere
Main.java
module-info.java
module my.donor {
// nothing is exported here
}
Cowboy.java
public class Cowboy {
public String say() { return "eee-ha"; }
}
Main.java
import my.project.first.Cowboy;
public class Main {
public static void main(String[] args) {
Cowboy g = new Cowboy();
System.out.println(g.say());
}
}
The class from my.project.first
package is supposed to be used in my.donor
module only and be invisible outside the module.
Unexpectedly it is visible outside the module as if there were an exports my.project.first;
line, so the class Main
can be compiled.
The way to restrict visibility is to make sub-recipient
a java module, adding module-info.java
, then everything inside of sub-donor
becomes hidden as expected. Of course, for any other non-modular project every public class in my.donor
module remains visible.
Please see a minimal working example on GitHub.
I am curious whether it is a bug or a conscious design.
What's the purpose of this approach?
答案1
得分: 2
TL;DR — Maven的问题。直接使用*javac
*编译可以正常工作。
详细版
> „…完整命令只是在父项目上运行 mvn compile…“
问题就在这里。Maven有一个令人惊讶的特性,只有当所有的子项目都有模块描述符时,它才会将子项目视为JPMS模块。
因此,因为只有你的*sub-donor
* Maven模块有JPMS模块描述符,你的两个子项目都被编译到了类路径上...
$ mvn -e -X compile
…
[INFO] 检测到变更 - 重新编译该模块!
[DEBUG] 类路径:
[DEBUG] sample-so-question-61888059-dev/sub-recipient/target/classes
[DEBUG] sample-so-question-61888059-dev/sub-donor/target/classes
…
[DEBUG] 命令行选项:
[DEBUG] -d sample-so-question-61888059-dev/sub-recipient/target/classes -classpath sample-so-question-61888059-dev/sub-recipient/target/classes;sample-so-question-61888059-dev/sub-donor/target/classes;…
…
因为两者都在类路径上,sub-donor
子项目的模块描述符变得无效。
不过,如果你自己动手直接运行*javac
*(假设你之前已经编译过 sub-donor
项目)...
javac --add-modules my.donor --module-path sample-so-question-61888059-dev/sub-donor/target/classes --class-path sample-so-question-61888059-dev/sub-recipient/target/classes -d sample-so-question-61888059-dev/sub-recipient/target/classes sample-so-question-61888059-dev/sub-recipient/src/main/java/my/somewhere/*.java
那么*javac
* 确实会像有意设计的那样报错...
sample-so-question-61888059-dev/sub-recipient/src/main/java/my/somewhere/Main.java:3: 错误: 包 my.project.first 不可见
import my.project.first.Cowboy;
^
(包 my.project.first 在模块 my.donor 中声明,但未导出)
1 个错误
英文:
TL;DR — Maven's yer problem. Compiling with javac
directly works as expected.
The long-winded version
> „…Full command is just mvn compile on the parent project…“
And therein lies your problem. Maven has a surprising feature where it will only treat a subproject as a JPMS module IFF all of the subprojects have module descriptors.
So because only your sub-donor
Maven module has the JPMS module descriptor, both of your subprojects are getting compiled on the classpath…
$ mvn -e -X compile
…
[INFO] Changes detected - recompiling the module!
[DEBUG] Classpath:
[DEBUG] sample-so-question-61888059-dev/sub-recipient/target/classes
[DEBUG] sample-so-question-61888059-dev/sub-donor/target/classes
…
[DEBUG] Command line options:
[DEBUG] -d sample-so-question-61888059-dev/sub-recipient/target/classes -classpath sample-so-question-61888059-dev/sub-recipient/target/classes;sample-so-question-61888059-dev/sub-donor/target/classes;…
…
And because both are on the classpath, the module descriptor of the sub-donor
subproject is rendered null and void.
If you take matters into your own hands though and run javac
directly (assuming you've compiled the sub-donor
project beforehand)…
javac --add-modules my.donor --module-path sample-so-question-61888059-dev/sub-donor/target/classes --class-path sample-so-question-61888059-dev/sub-recipient/target/classes -d sample-so-question-61888059-dev/sub-recipient/target/classes sample-so-question-61888059-dev/sub-recipient/src/main/java/my/somewhere/*.java
Then javac
does indeed balk as consciously designed…
sample-so-question-61888059-dev/sub-recipient/src/main/java/my/somewhere/Main.java:3: error: package my.project.first is not visible
import my.project.first.Cowboy;
^
(package my.project.first is declared in module my.donor, which does not export it)
1 error
专注分享java语言的经验与见解,让所有开发者获益!
评论