14.8 模块声明及子句
Java模块系统非常复杂,就像一个庞然大物。之前我们也提过,如果你想进一步了解Java模块系统,建议你选择一本专门讲述相应内容的著作来深入学习。不过,我们还是希望通过这一节的概述,让你了解模块声明语言中有哪些关键字,以及它们大致能做些什么。
前文已经介绍过,你可以通过模块指令声明一个模块。就像下面这段代码,它声明了一个名为com.iteratrlearning.application的模块:
module com.iteratrlearning.application {}
模块声明内部有什么?你已经学习了requires和exports子句,但是模块还提供了很多其他的子句,包括requires-transitive、exports-to、open、opens、uses和provides。下面一一介绍这些子句。
14.8.1 requires
requires子句可以在编译和运行时帮你设定你的模块对另一模块的依赖。譬如,模块com.iteratrlearning.application依赖于com.iteratrlearning.ui:
module com.iteratrlearning.application {requires com.iteratrlearning.ui;}
执行这条子句的结果是模块com.iteratrlearning.application只能访问模块com.iteratrlearning.ui中声明为公有的类型。
14.8.2 exports
exports子句可以将某些包声明为公有类型,提供给其他的模块使用。默认情况下,模块中所有的包都不导出。只能通过显式声明的方式导出包,让你对模块的封装性有了更严格的控制。下面这个例子中,com.iteratrlearning.ui.panels和com.iteratrlearning.ui.widgets都被导出了。(注意:exports接受的参数是包名,而requires接受的参数是模块名。虽然二者都采用了类似的命名模式,但仍有区别。)
module com.iteratrlearning.ui {requires com.iteratrlearning.core;exports com.iteratrlearning.ui.panels;exports com.iteratrlearning.ui.widgets;}
14.8.3 requires的传递
你可以声明一个模块能够使用另一个模块依赖的公有类型的包。譬如,你可以修改模块com.iteratrlearning.ui的声明,将requires子句变更为requires-transitive达到该效果,如下所示:
module com.iteratrlearning.ui {requires transitive com.iteratrlearning.core;exports com.iteratrlearning.ui.panels;exports com.iteratrlearning.ui.widgets;}module com.iteratrlearning.application {requires com.iteratrlearning.ui;}
这段声明的效果是模块com.iteratrlearning.application可以访问com.iteratrlearning.core导出的公有类型的包。当一个被依赖的模块(譬如这个例子中的com.iteratrlearning.ui)返回该模块自身依赖的模块(com.iteratrlearning.core)的类型时,传递性就非常有价值了。想象一下,如果需要在模块com.iteratrlearning.application中重复声明com.iteratrlearning.core的依赖,也是很烦人的事情。这个问题被transitive解决了。现在,依赖于com.iteratrlearning.ui的包自动地就能访问com.iteratrlearning.core模块。
14.8.4 exports to
你对模块的可见性可以做进一步的控制,通过exports to结构,可以限制哪些用户能访问哪些导出的包。通过调整模块声明,你可以对14.8.2节中的例子做更细粒度的控制,只允许com.iteratrlearning.ui.widgets访问com.iteratrlearning.ui.widgetuser,如下所示:
module com.iteratrlearning.ui {requires com.iteratrlearning.core;exports com.iteratrlearning.ui.panels;exports com.iteratrlearning.ui.widgets tocom.iteratrlearning.ui.widgetuser;}
14.8.5 open和opens
模块声明中使用open限定符能够让其他模块以反射的方式访问它所有的包。open限定符在模块的可见性方面没有特别的效果,唯一的作用就是允许对模块进行反射访问,如下所示:
open module com.iteratrlearning.ui {}
Java 9之前,你就能借助反射查看对象的私有状态。换句话说,没有什么是真正完全封装的。对象关系映射(object-relational mapping,ORM)工具,譬如Hibernate,就经常利用这种能力直接访问和修改对象的状态。默认情况下,Java 9不允许执行反射了。前面代码中的open子句提供了一种途径,允许在需要的时候进行反射。
你可以按照需要使用open子句对模块中的某个包执行反射,而不是对整个模块执行反射。此外,你还可以像exports-to限制导出模块的访问那样,为open添加to限定符,限制哪些模块可以执行反射访问。
14.8.6 uses和provides
如果你熟悉服务和ServiceLoader,接下来的内容可能就轻车熟路了。在Java模块系统中,你也可以使用provides子句创建服务供应方,使用users子句创建服务消费者。然而这个主题有点复杂,超出了本章的范畴。如果你对整合模块以及服务装载器感兴趣,建议你参考更广泛的学习资源,譬如本章前面提到的由Nicolai Parlog编写的The Java Module System。
