JLS9学习之模块声明

想了解一下java9 module的语法,找到JLS(Java Language Specification),直奔7.7章节Module Declarations。

7.7 模块声明

模块的声明定义了一个已命名模块(还有一种unnamed module,未命名模块,见7.7.5)。已命名模块中指明了本模块依赖哪些其他模块,即本模块的代码能用到哪些其他模块中的类和接口;还指明了本模块中的哪些包是导出的开放的,从而指明了本模块中的哪些类和接口是依赖本模块的模块可以用的。

requires命令表达的是一种“依赖关系”,与这个命令后跟的具体模块是否存在无关。(???so?)

“依赖”是指通过解析后能枚举出的可见的模块,解析的具体规则详见api doc的java.lang.module包中的描述(看了下挺重要的,也搬过来翻一下)。

总得来说,更关心的是“依赖关系”,而不是具体的“依赖”。(???meaning?)

------- 跳跃至api doc的java.lang.module包,来看一下这个resolution的详情 -------

什么是Resolution呢?Resolution就是计算模块之间谁依赖谁的过程(好吧,跟没说一样)。这个过程在编译时和运行时都会进行。

Resolution分两步走。第一步,先递归地列举出requires命令指定的一系列模块,若所有列举出的模块都是可见的,那么则进行第二步,也就是计算出这些模块的可读性图。这个可读性图展现了模块之间的依赖关系,而这些依赖关系则控制了模块之间的访问界限。

步骤一:递归列举

递归列举会拿到一些模块的名字,并查看这些模块的模块声明,然后对于每个模块声明,递归地进行如下列举过程:

  • 带有transitive修饰符的requires命令后的模块名

  • 视主机系统酌情而定(??), 不含transitive修饰符的requires命令后的模块名

系统会在可见的模块中查看其模块声明。什么模块是可见的取决于具体实现方式(???such as?)。在这些可见的模块中可能会包括一些显式声明的模块,即含有module-info.java或module-info.class文件的模块,以及一些隐式声明的模块,即自动模块(??what ?)。因为自动模块是没有显式的模块声明的,它没有requires命令,然后它自己是有可能被一些显式声明的requires命令中依赖到的。

根模块,即被初始放入这个算法中的模块们,是取决于具体实现方式(??such as)。根模块中可能包含自动模块。

若至少有一个自动模块被这个算法列举出来,那么所有可见的自动模块都必须被列举出来,不管是否被显式声明的requires命令提到过。

当出现如下情形时,解析将失败:

  • 任一个根模块是不可见的
  • 任一个被含有transitive修饰符的requires命令所指定的模块是不可见的
  • 视主机系统酌情而定,任一个被无transitive修饰符的requires命令所指定的模块是不可见的
  • 此算法在本步骤列举出同一个模块名两次。这说明在requires指令中存在循环嵌套,无论是否使用了transitive修饰符。

若上述情形均未出现,则进行下一步骤。

步骤二:计算可读性图

requires命令表达了一个模块依赖于一些其他模块。transitive修饰符的作用是使一些额外的模块也依赖这些其他模块。若模块M requires transitive N,那么不止模块M依赖N,所有依赖M的模块也依赖N。这可以允许对模块M进行重构,可将其部分或全部内容移至新模块N中,同时不影响那些有requires M命令的模块。

模块依赖是通过可读性图来呈现的。可读性图是一个有向图,其顶点为步骤一中列举出的模块,其边代表了每对模块之间的可读性。可读性图的边的具体描述如下:

首先,可读性是由列举出的模块中的requires命令所决定的,不考虑transitive修饰符。

  • 当列举出的模块A requires B时,A可以读B
  • 当列举出的模块X为自动模块时,X可以读到所有其他列举出的模块,仿佛X requres所有其他模块了个遍。

其次,可读性考虑到transitive修饰符也有如下增强。

  • 当列举出的模块A可以读B时:

    • 若B requires transitive C, 那么A 可读 C 并且可读 B。这是递归的:由于A 可读 C, 当C requires transitive D时,A 也可读 D,并且可读 C和B。

    • 若B是个自动模块,则A 可读到所有列举出的自动模块,仿佛自动模块requires transitive所有其他自动模块了个遍。

最后,每个模块都可读它自己。

当如下情形出现在可读性图时,解析将失败:

  • 一个模块可读到两个或以上重名的模块。这包括一个模块读到了另一个和自己名字一样的模块的情形。
  • 两个或多个模块面向一个可以同时读到它们的模块导出了一个重名的包。这包括一种情形,即一个包含名为P的包的模块M,读到了另一个面向M导出了同样名为P的包的模块。
  • 一个模块M声明了它用到了p.S或提供了p.S,但包p既不在M中,又不在M能读到的模块的已导出的包中。

若未出现如上情形,则解析过程成功,解析的结果即为可读性图。

------  解析规则到此为止啦,再来看看另外几个其中提到的概念  ------

Root modules

The set of root modules at compile-time is usually the set of modules being compiled. At run-time, the set of root modules is usually the application module specified to the 'java' launcher. When compiling code in the unnamed module, or at run-time when the main application class is loaded from the class path, then the default set of root modules is implementation specific (In the JDK implementation it is the module "java.se", if observable, and every observable module that exports an API).

根模块

编译时的根模块常常是正在被编译的模块。在运行时,根模块常为java命令中所指定的应用模块。当编译未命名模块中的代码,或在运行时主要的应用class文件都是从class path中载入时,那么默认的根模块则由具体实现而定,JDK的实现为若可见的话,java.se是根模块,以及所有导出了API的可见模块。

Observable modules

The set of observable modules at both compile-time and run-time is determined by searching several different paths, and also by searching the compiled modules built in to the environment. The search order is as follows:

  1. At compile time only, the compilation module path. This path contains module definitions in source form.

  2. The upgrade module path. This path contains compiled definitions of modules that will be observed in preference to the compiled definitions of any upgradeable modules that are present in (3) and (4). See the Java SE Platform for the designation of which standard modules are upgradeable.

  3. The system modules, which are the compiled definitions built in to the environment.

  4. The application module path. This path contains compiled definitions of library and application modules.

可见模块

在编译时和运行时的可见模块都是通过搜索若干不同的路径,以及搜索构建至环境中的被编译模块所决定的。搜索顺序如下:

  1. 当仅编译时,为编译模块路径。这个路径包含了源文件形式的模块定义。.

  2. 升级模块路径。这个路径包含了已编译的模块定义,这些模块定义相较于(3)和(4)出现的可升级模块会先被发现. 哪些标准模块是指定可升级的详见Java SE Platform。

  3. 系统模块,即构建至环境中的已编译定义。

  4. 应用模块路径。这个路径包含了已编译的库和应用模块的定义。

'requires' directives with 'static' modifier

'requires' directives that have the 'static' modifier express an optional dependence at run time. If a module declares that it 'requires static M' then resolution does not search the observable modules for M to satisfy the dependency. However, if M is recursively enumerated at step 1 then all modules that are enumerated and `requires static M` will read M.

带有static修饰符的requires命令

带有static修饰符的requires命令表达了一种运行时的可选依赖。若一个模块声明了它requires static M,则解析过程不会去搜索这个名为M的模块。然而,若M在步骤一中被递归列举出来了,那么所有声明了requires static M的模块都会读到M。

Completeness

Resolution may be partial at compile-time in that the complete transitive closure may not be required to compile a set of modules. Minimally, the readability graph that is constructed and validated at compile-time includes the modules being compiled, their direct dependences, and all implicitly declared dependences (requires transitive).

At run-time, resolution is an additive process. The recursive enumeration at step 1 may be relative to previous resolutions so that a root module, or a module named in a 'requires' directive, is not enumerated when it was enumerated by a previous (or parent) resolution. The readability graph that is the result of resolution may therefore have a vertex for a module enumerated in step 1 but with an edge to represent that the module reads a module that was enumerated by previous (or parent) resolution.

完整性

在编译时的解析过程有可能只进行了一部分,因为可能不需要一个完整的依赖传递闭环来实现模块的编译。最简情形为,在编译时构建和校验的可读性图,包含了正在被编译的模块,和它们的直接依赖,以及所有通过requires transitive隐式声明的依赖。

在运行时,解析过程是有累积性的。步骤一中递归列举有可能和之前的解析过程有关,因为某个根模块,或requires命令中提到的模块,不是被当前的列举过程发现的,而是被之前的或父级的解析过程中列举出来的。所以可读性图中可能包含一种顶点,其代表的是当前列举过程发现的模块,但含有一个指向之前或父级解析过程中列举出的模块的边。

--------  绕了一大圈,回到JLS9 7.7章节中 --------

ModuleDeclaration:

{Annotation} [openmodule Identifier {. Identifier{ {ModuleDirective}

A module declaration introduces a module name that can be used in other module declarations to express relationships between modules. A module name consists of one or more Java identifiers (§3.8) separated by "." tokens.

There are two kinds of modules: normal modules and open modules. The kind of a module determines the nature of access to the module's types, and the members of those types, for code outside the module.

A normal module, without the open modifier, grants access at compile time and run time to types in only those packages which are explicitly exported.

An open module, with the open modifier, grants access at compile time to types in only those packages which are explicitly exported, but grants access at run time to types in all its packages, as if all packages had been exported.

模块声明语法:

{注解} [open修饰符] module关键字 模块标识符(即模块名) { {模块命令(requires,exports等)} }

模块声明说明了模块名,其可用在其他模块声明中来表达模块之间的关系。模块名由一个或多个java标识符组成,由"."号隔开。

模块分为两种,普通模块和开放模块。模块的种类决定了模块之外的代码访问模块中的类,以及它们的成员时的访问特性。

普通模块,即不含open修饰符的,在编译时和运行时,都只授予那些显式导出的包的访问权限。

开放模块,即带有open修饰符的,在编译时只授予显式导出包的访问权限,但在运行时授予其下所有包的访问权限,仿佛全被导出似的。

For code outside a module (whether the module is normal or open), the access granted at compile time or run time to types in the module's exported packages is specifically to the public and protected types in those packages, and the public and protected members of those types (§6.6). No access is granted at compile time or run time to types, or their members, in packages which are not exported. Code inside the module may access public and protected types, and the public and protected members of those types, in all packages in the module at both compile time and run time.

Distinct from access at compile time and access at run time, the Java SE Platform provides reflective access via the Core Reflection API (§1.4). A normal module grants reflective access to types in only those packages which are explicitly exported or explicitly opened (or both). An open module grants reflective access to types in all its packages, as if all packages had been opened.

For code outside a normal module, the reflective access granted to types in the module's exported (and not opened) packages is specifically to the public and protected types in those packages, and the public and protected members of those types. The reflective access granted to types in the module's opened packages (whether exported or not) is to all types in those packages, and all members of those types. No reflective access is granted to types, or their members, in packages which are not exported or opened. Code inside the module enjoys reflective access to all types, and all their members, in all packages in the module.

For code outside an open module, the reflective access granted to types in the module's opened packages (that is, all packages in the module) is to all types in those packages, and all members of those types. Code inside the module enjoys reflective access to all types, and all their members, in all packages in the module.

对于模块外的代码,无论模块是普通的还是开放的,对于被导出包中的类的访问权限,编译和运行时,仅限于这些包中的public和protected的类,以及这些类中的public和protected的成员。未被导出的包中的类,编译和运行时,都是不可访问的。模块内的代码,编译和运行时,可以访问到模块下全部包的public和protected的类,以及这些类的public和protected成员。

区别于编译时和运行时的访问,java SE提供了反射访问机制。普通模块仅对显式导出或显式开放的包中的类提供反射访问权限。开放模块对其下全部包中的类均提供反射访问权限,仿佛所有包都是开放的。

对于普通模块,其外的代码的反射访问权限,对于导出但非开放的包而言,仅限于包内的public和protected类,以及这些类的public和protected成员。对于开放的包,无论是否导出,其访问权限为包内的全部类以及其全部成员。对于既未导出又非开放的包,不提供任何对于其中的类和成员的访问权限。而模块内的代码,则享有模块内全部包下全部类及其成员的反射访问权限。

对于开放模块,其外的代码的反射访问权限为,模块中开放的包中的全部类及其成员,也就是模块中全部包的全部类和成员。模块内的代码同样享受完全的访问权限。

The directives of a module declaration specify the module's dependences on other modules (via requires§7.7.1), the packages it makes available to other modules (via exports and opens§7.7.2), the services it consumes (via uses§7.7.3), and the services it provides (via provides§7.7.4).

ModuleDirective:

requires {RequiresModifierModuleName ; 
exports PackageName [to ModuleName {, ModuleName}] ; 
opens PackageName [to ModuleName {, ModuleName}] ; 
uses TypeName ; 
provides TypeName with TypeName {, TypeName;

RequiresModifier:

(one of) 
transitive static

模块声明中的指令,通过requires指明了本模块对于其他模块的依赖,通过exports和opens指明了本模块中可被其他模块使用的包,通过uses指明了本模块用到的服务,以及通过provides指明了本模块提供的服务。

模块指令语法:(终于到了干货和重点)

requires {requires修饰符} 模块名;

exports 包名 [to 模块名 {, 模块名}];

opens 包名 [to 模块名 {, 模块名}];

uses 类名;

provides 类名 with 类名 {, 类名};

requires修饰符:

可选其一

transitive static

If and only if packages are stored in a file system (§7.2), the host system may choose to enforce the restriction that it is a compile-time error if a module declaration is not found in a file under a name composed of module-info plus an extension (such as .java or .jav).

当且仅当包存储在文件系统中时,当主机系统未在以module-info为名称的文件中找到其模块定义时,会强制约束其出现一个编译时错误。

(哇,代码示例终于来了!!然而copy不了哦,哈哈哈~)

为助于理解,习惯上会将模块声明中的命令进行分组书写。这样子与模块相关的命令requires,可以很直观的与和包相关的命令exports或opens区分开来,同时和与服务相关的命令uses或provides区分开来。

当模块为开放模块时,要避免使用opens命令。

我们鼓励java语言的开发工具对requires transitive命令和无条件的exports命令进行高亮展示,由于它们是构成一个模块的首要API。

7.7.1. Dependences

The requires directive specifies the name of a module on which the current module has a dependence.

requires directive must not appear in the declaration of the java.base module, or a compile-time error occurs, because it is the primordial module and has no dependences (§8.1.4).

If the declaration of a module does not express a dependence on the java.base module, and the module is not itself java.base, then the module has an implicitly declared dependence on the java.base module.

The requires keyword may be followed by the modifier transitive. This causes any module which requires the current module to have an implicitly declared dependence on the module specified by the requires transitive directive.

The requires keyword may be followed by the modifier static. This specifies that the dependence, while mandatory at compile time, is optional at run time.

It is a compile-time error if more than one requires directive in a module declaration specifies the same module name.

It is a compile-time error if resolution, as described in the java.lang.module package specification, with the current module as the only root module, fails for any of the reasons described in the java.lang.module package specification.

For example, if a requires directive specifies a module that is not observable, or if the current module directly or indirectly expresses a dependence on itself.

If resolution succeeds, then its result specifies the modules that are read by the current module. The modules read by the current module determine which ordinary compilation units are visible to the current module (§7.3). The types declared in those ordinary compilation units (and only those ordinary compilation units) may be accessible to code in the current module (§6.6).

The Java SE Platform distinguishes between named modules that are explicitly declared (that is, with a module declaration) and named modules that are implicitly declared (that is, automatic modules). However, the Java programming language does not surface the distinction: requires directives refer to named modules without regard for whether they are explicitly declared or implicitly declared.

While automatic modules are convenient for migration, they are unreliable in the sense that their names and exported packages may change when their authors convert them to explicitly declared modules. A Java compiler is encouraged to issue a warning if a requires directive refers to an automatic module. An especially strong warning is recommended if the transitive modifier appears in the directive.

7.7.1 依赖

requires命令指定了当前模块所依赖的模块的名字。

requires命令不允许出现在java.base模块的定义中,否则将出现一个编译错误,因为java.base是最初始的一个模块,且不依赖任何模块。

如果一个模块没有显式声明依赖java.base,这个模块又不是java.base,那么这个模块则隐式依赖java.base。(所有模块都默认依赖java.base)

requires关键字可跟随一个transitive修饰符。这会导致每个依赖当前模块的模块,都会对requires transitive后指定的模块有隐式依赖。

requires关键字可跟随一个static修饰符。这表明这个依赖虽然在编译时是强制性的,但运行时是可选的。(??好像跟之前介绍不相符)

当一个模块定义中出现多个对同一模块的requires命令时,将出现编译错误。

当当前模块作为解析过程的唯一根模块,而解析过程又失败了时,将出现编译错误。

例如,一个模块定义中requires了一个不可见的模块,或当前模块直接或间接的依赖了自己。

若解析成功,那么其结果会指出当前模块读到了哪些模块。被当前模块读到的那些模块会决定哪些编译单元面向当前模块是可见的。当前模块中的代码则可以访问到那些编译单元中的类。

Java SE平台会区分显式声明的模块,即有模块声明的,与隐式声明的模块,即自动模块。然而,java语言却不体现这种区分,requires命令所指向的模块名是不考虑其是隐式的还是显式的。

虽说自动模块是便于迁移的,但考虑到其作者在将其转化为显式模块时有可能改变其模块名和包名,自动模块是并不可靠的。我们鼓励java编译器在发现requires命令指向的是自动模块时,发出一个警告。特别在出现transitive修饰符时,更要强烈警告。

又到了代码示例时间~

示例 7.1.1.1  requires transitive命令的解析

设想有如下四个模块声明:

代码 图 代码 图 代码。。。。-_-

被m.D模块导出的包p的定义如下:

代码 图。。

然后m.A模块中的包client,引用到了包p中的public类Point。

代码 图。。

这些模块将通过如下命令进行编译,这里假定当前目录下的每个模块都有一个与自己同名的子目录。

命令 命令 命令 图。。

可以通过如下命令来运行client.Test程序

命令 图。。

m.A中的代码对m.D中public类Point的引用是合法的,因为m.A可读到m.D,m.D导出的包中包含Point。解析过程是通过如下来认定m.A可读到m.D的:

  • m.A requires m.B,所以m.A可读到m.B
  • 由于m.A可读m.B,m.B又requires transitive m.C,故解析认定m.A可读m.C
  • 同样的,m.A可读m.C,m.C requires transitive m.D,故m.A可读m.D

一个模块可以通过多级的依赖读到另一个模块,从而支持一些任意数量的重构操作。一旦当模块被发布供使用者使用了,模块的作者就不能轻易改动其名称和API了,但却可将其中的内容重构至这个模块用到的模块中,即requires transitive的模块。就上述示例而言,包p在最初可能是被m.B模块导出的,因为m.A模块中的定义是m.A依赖m.B,但重构操作可能导致m.B中的一些内容被移至了m.C和m.D中。这时通过一系列的requires transitive命令,可使b,c,d都保留着对包p的访问,从而保证a中用到包p的代码不受影响,并且不需要修改任何a模块的依赖声明。这里注意,d中的包p并没被b,c重导出过,反而a是可直接读到d的。

7.7.2. Exported and Opened Packages

The exports directive specifies the name of a package to be exported by the current module. For code in other modules, this grants access at compile time and run time to the publicand protected types in the package, and the public and protected members of those types (§6.6). It also grants reflective access to those types and members for code in other modules.

The opens directive specifies the name of a package to be opened by the current module. For code in other modules, this grants access at run time, but not compile time, to the publicand protected types in the package, and the public and protected members of those types. It also grants reflective access to all types in the package, and all their members, for code in other modules.

It is a compile-time error if the package specified by exports is not declared by a compilation unit associated with the current module (§7.3).

It is permitted for opens to specify a package which is not declared by a compilation unit associated with the current module. (If the package should happen to be declared by an observable compilation unit associated with another module, the opens directive has no effect on that other module.)

It is a compile-time error if more than one exports directive in a module declaration specifies the same package name.

It is a compile-time error if more than one opens directive in a module declaration specifies the same package name.

It is a compile-time error if an opens directive appears in the declaration of an open module.

If an exports or opens directive has a to clause, then the directive is qualified; otherwise, it is unqualified. For a qualified directive, the public and protected types in the package, and their public and protected members, are accessible solely to code in the modules specified in the to clause. The modules specified in the to clause are referred to as friends of the current module. For an unqualified directive, these types and their members are accessible to code in any module.

It is permitted for the to clause of an exports or opens directive to specify a module which is not observable (§7.7.6).

It is a compile-time error if the to clause of a given exports directive specifies the same module name more than once.

It is a compile-time error if the to clause of a given opens directive specifies the same module name more than once.

7.7.2 导出的和开放的包

exports命令用于指明当前模块导出的包的名字。对于其他模块中的代码,这授予了它们访问这个包中public和protected类,以及这些类的public和protected成员的权限,在编译和运行时。同时还授予了其他模块的代码对于这些类和成员的反射访问权限。

opens命令用于指明当前模块开放的包的名字。对于其他模块的代码,这授予了它们在运行时,非编译时,对于包中public和protected类,以及这些类的public和protected成员的访问权限。同时还授予了其他模块的代码对于此包下全部类和全部成员的反射访问权限。

当exports的包未被任意当前模块中的编译单元声明过时,将出现一个编译错误。

而opens后的包是允许没有被当前模块中的编译单元声明过的。如果这个包刚好被其他模块中的一个可见编译单元声明过的话,则opens命令对那个模块不起任何作用。

当同一个包被多次exports时,会出现编译错误。

当同一个包被多次opens时,会出现编译错误。

当opens命令出现在一个开放模块中时,会出现编译错误。

当一个exports或opens命令有一个to语句时,则这个命令是受限的,反之为不受限的。对于一个受限命令而言,只有to语句中指定的模块中的代码才能访问到此包中的public和protected类及其public和protected成员。to语句中所指定的模块被看作是当前模块的"朋友"。对于不受限命令而言,任意模块中的代码都能访问到以上类和成员。

to语句是允许指定一个不可见的模块的。

当to语句中多次出现同一个模块名时,会出现编译错误。

7.7.3. Service Consumption

The uses directive specifies a service for which the current module may discover providers via java.util.ServiceLoader.

The service must be a class type, an interface type, or an annotation type. It is a compile-time error if a uses directive specifies an enum type (§8.9) as the service.

The service may be declared in the current module or in another module. If the service is not declared in the current module, then the service must be accessible to code in the current module (§6.6), or a compile-time error occurs.

It is a compile-time error if more than one uses directive in a module declaration specifies the same service.

7.7.3 服务消费

uses命令用于指明一个服务,这个服务是由当前模块通过java.util.ServiceLoader去探索服务提供者而发现的。

这个服务必须是一个类,接口或注解类型。当uses指定的服务为枚举类型时,会出现编译错误。

这个服务可以在当前模块或其他模块中。若其不在当前模块,那么当前模块中的代码必须能访问到这个服务,否则将出现编译错误。

当同一个服务被多次uses时,会出现编译错误。

7.7.4. Service Provision

The provides directive specifies a service for which the with clause specifies one or more service providers to java.util.ServiceLoader.

The service must be a class type, an interface type, or an annotation type. It is a compile-time error if a provides directive specifies an enum type (§8.9) as the service.

The service may be declared in the current module or in another module. If the service is not declared in the current module, then the service must be accessible to code in the current module (§6.6), or a compile-time error occurs.

Every service provider must be a class type or an interface type, that is public, and that is top level or nested static, or a compile-time error occurs.

Every service provider must be declared in the current module, or a compile-time error occurs.

If a service provider explicitly declares a public constructor with no formal parameters, or implicitly declares a public default constructor (§8.8.9), then that constructor is called the provider constructor.

If a service provider explicitly declares a public static method called provider with no formal parameters, then that method is called the provider method.

If a service provider has a provider method, then its return type must i) either be declared in the current module, or be declared in another module and be accessible to code in the current module; and ii) be a subtype of the service specified in the provides directive; or a compile-time error occurs.

While a service provider that is specified by a provides directive must be declared in the current module, its provider method may have a return type that is declared in another module. Also, note that when a service provider declares a provider method, the service provider itself need not be a subtype of the service.

If a service provider does not have a provider method, then that service provider must have a provider constructor and must be a subtype of the service specified in the provides directive, or a compile-time error occurs.

It is a compile-time error if more than one provides directive in a module declaration specifies the same service.

It is a compile-time error if the with clause of a given provides directive specifies the same service provider more than once.

7.7.4 服务供应

provides命令用于指定一个服务,与with语句所指定的一个或多个服务提供者,同时提供给java.util.ServiceLoader。

provides命令所指定的服务必须为类、接口或注解类型。当其为枚举类型时,会出现编译错误。

provides所指定的服务可声明在当前模块或其他模块。当未声明在当前模块时,当前模块的代码必须能访问到这个服务,否则会出现编译错误。

每一个with语句指定的服务提供者,都必须是public的类或接口类型,且是顶级的或嵌套的static的,否则会出现编译错误。

每一个服务提供者都必须声明在当前模块中,否则会出现编译错误。

若一个服务提供者显式声明了无参的public构造器,或隐式声明了默认的public构造器,则这个构造器被称为服务提供者构造器。

若一个服务提供者显式声明了一个无参的名为provider的public static方法,则这个方法被称为服务提供者方法。

若一个服务提供者有提供者方法时,那么其返回值类型必须:1.要么声明在当前模块,要么声明在当前模块代码可访问到的其他模块 2.是provides命令所指定的服务的子类。否则将出现编译错误。

虽说服务提供者必须声明在当前模块,可其提供者方法的返回值的类型是可以声明在其他模块的。同时,注意到当服务提供者有提供者方法时,这个服务提供者本身不需要是服务的子类。

当服务提供者没有提供者方法时,则其必须有一个构造器,且服务提供者本身必须为服务的子类,否则会出现编译错误。

(规则略乱,总之就是调用这个服务提供者的方法时,无论是provider method还是constructor,必须返回一个其服务的子类)

当同一个服务被多次provides时,编译错误。

当同一个服务提供者被with多次时,编译错误。

7.7.5. Unnamed Modules

An observable ordinary compilation unit that the host system does not associate with a named module (§7.3) is associated with an unnamed module.

Unnamed modules are provided by the Java SE Platform in recognition of the fact that programs developed prior to Java SE 9 could not declare named modules. In addition, the reasons for the Java SE Platform providing unnamed packages (§7.4.2) are largely applicable to unnamed modules.

An implementation of the Java SE Platform must support at least one unnamed module. An implementation may support more than one unnamed module, but is not required to do so. Which ordinary compilation units are associated with each unnamed module is determined by the host system.

The host system may associate ordinary compilation units in a named package with an unnamed module.

The rules for unnamed modules are designed to maximize their interoperation with named modules, as follows:

  • An unnamed module reads every observable module (§7.7.6).

    By virtue of the fact that an ordinary compilation unit associated with an unnamed module is observable, the associated unnamed module is observable. Thus, if the implementation of the Java SE Platform supports more than one unnamed module, every unnamed module is observable; and each unnamed module reads every unnamed module including itself.

    However, it is important to realize that the ordinary compilation units of an unnamed module are never visible to a named module (§7.3) because no requires directive can arrange for a named module to read an unnamed module. The Core Reflection API of the Java SE Platform may be used to arrange for a named module to read an unnamed module at run time.

  • An unnamed module exports every package whose ordinary compilation units are associated with that unnamed module.

  • An unnamed module opens every package whose ordinary compilation units are associated with that unnamed module.

7.7.5 未命名模块

当一个可见的编译单元没有被主机系统与已命名模块关联起来时,它将被关联到一个未命名模块。

Java SE平台提供未命名模块的原因是因为,java 9之前版本所开发的程序是不能声明模块的。另外,java SE平台提供未命名包的大多数原因都适用于未命名模块。

Java SE平台的实现必须至少支持一个未命名模块。也可以支持多个,但并不强制要求。哪些普通编译单元会被关联到哪些未命名模块上是由主机系统决定的。

主机系统可将一个已命名包中的普通编译单元关联到未命名模块上。

未命名模块规则的制定是为了最大化与已命名模块的相互操作性,如下:

  • 未命名模块可读到所有可见模块。

由于关联未命名模块的编译单元都是可见的,故其关联的未命名模块也是可见的。所以,当java SE平台的实现支持多个未命名模块时,所有未命名模块都是可见的。且每个未命名模块都能读到所有未命名模块,包括其本身。

然而,有很重要的一点要知道,那就是未命名模块中的编译单元对于已命名模块来说总是不可见的。因为已命名模块中不可能通过requires命令去依赖未命名模块。已命名模块可以在运行时使用反射API去访问未命名模块。

  • 未命名模块会导出其全部包
  • 未命名模块会开发其全部包

7.7.6. Observability of a Module

A module is observable if at least one of the following is true:

  • A modular compilation unit containing the declaration of the module is observable (§7.3).

  • An ordinary compilation unit associated with the module is observable.

7.7.6 模块的可见性

当下述至少一条成立时,模块是可见的:

  • 包含模块声明的模块编译单元是可见的
  • 关联至此模块的一个普通编译单元是可见的

==========  JLS9 7.7至此告一段落,看完理论剩下的就是实践了,走起  ===========

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值