Java9中的需求和需求传递语句之间的区别是什么?

在模块声明中,需要需要传递模块语句的区别是什么?

例如:

module foo {
requires java.base;
requires transitive java.compiler;
}
22279 次浏览

Primary difference between the two is the access of a dependent module from one to another.

If one module exports a package containing a type whose signature refers to a package in a second module then the declaration of the first module should include a requires transitive dependence upon the second. This will ensure that other modules that depend upon the first module will automatically be able to read the second module and, hence, access all the types in that module’s exported packages.


So let's say for your use case :-

module foo {
requires java.base;
requires transitive java.compiler;
}

~> Any module that depends upon the foo module will automatically read the java.compiler module

~> On the other hand to access the module java.base, they must specify a requires clause again.

module bar {
requires foo; // java.compiler is available to read
requires java.base; // still required
}

requires describes the process of resolution on how modules are dependent on each other.

Quoting line

A 'requires' directive (irrespective of 'transitive') expresses that one module depends on some other module. The effect of the 'transitive' modifier is to cause additional modules to also depend on the other module. If module M 'requires transitive N', then not only does M depend on N, but any module that depends on M also depends on N. This allows M to be refactored so that some or all of its content can be moved to a new module N without breaking modules that have a 'requires M' directive.

In short :

requires - M module depends on some other module N.

requires transitive - additional modules implicitly depends on the other module. Eg:, If M module depends on N, and other module P depends on M. Then, it is implicitly dependent on N as well.

Readability recap

If module bar requires module drink, then the module system...

  • enforces the presence of drink (called reliable configuration)
  • allows bar to read drink (called readability)
  • allows code in bar to access public classes in exported packages in drink (called accessibility)

Exactly the same happens if bar requires transitive drink - drink must be present, can be read and accessed. In fact, for bar and drink the transitive keyword doesn't change anything.

Implied readability

The modules depending on bar are the ones that are impacted by transitive: Any module that reads bar can also read drink. In other words readability of drink is implied (which is why this is called implied readability). A consequence is that customer can access drink's types.

So if bar requires transitive drink and customer requires bar, then customer can read drink even though it doesn't explicitly depend on it.

Use cases

But why? Imagine you have a module whose public API accepts or returns another module's type. Let's say the bar module publicly returns instances of Drink, an interface from the drink module:

// in module _bar_
public class Bar {


// `Drink` comes from the module _drink_,
// which _bar_ requires
public Drink buyDrink() { /* ... */ }


}

In this example, bar uses a regular requires for drink. Now say, customer depends on bar, so all its code can call Bar::buyDrink. But what happens when it does?

The module system complains that customer does not read drink and can hence not access Drink. To fix that, customer would also have to depend on drink. What a chore! How useless is a bar that you can't use straight away?

customer requires bar requires drink - but how does customer read drink?

For this reason, implied readability was introduced: to make a module that uses another module's types in its own public API instantly usable without requiring the caller to hunt down and require all involved modules.

So if bar requires transitive drink, customer can start buying drinks without having to require drink - require bar suffices. As it should.

Nicolai has explained in detail. I'm just giving a specific example from JDK code here. Consider the jdk.scripting.nashorn module. The module-info of this module is as follows:

http://hg.openjdk.java.net/jdk9/dev/nashorn/file/17cc754c8936/src/jdk.scripting.nashorn/share/classes/module-info.java

It has this line:

requires transitive java.scripting;

This is because jdk.scripting.nashorn module's own API in jdk.scripting.api.scripting package accepts/returns types from javax.script package of the java.scripting module. So jdk.scripting.nashorn tells the JMPS that any module that depends on jdk.scripting.nashorn automatically depends on java.scripting module as well!

Now, the same jdk.scripting.nashorn module uses this line:

    requires jdk.dynalink;

for another module jdk.dynalink. That is because none of the exported packages (the "API") from jdk.scripting.nashorn module uses types from jdk.dynalink module. The use of jdk.dynalink by jdk.scripting.nashorn is purely an implementation detail.

The Java Language Specification for Java 9 explains it in very simple terms. From the section on Module Dependences:

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

...

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

In other words:

  • if module X requires module Y,
  • and module Y requires transitive module Z,
  • then module X also (implicitly) requires module Z.

The term accessibility is ambiguous: you can access objects without access to their type. If an object is of type T that lies in a package that is not exported and if an "exported" code has a method that returns a T ... Then when invoking this method you get a handle on this T object (and you can invoke on it any methods that pertains to any type known to your code).

readability is also ambiguous: it does not mean your ClassLoader will always be unable to load the (unexported) T class.

the difference between requires and requires transitive has been explained above hence I will only add effects of requires transitive

  • if (module m requires transitive n) && (module L requires m) then module L cannot be compiled or executed without access to module n

  • if (module m requires transitive n) && (module L requires both m and n) then module L doesn't need to explicitly declare requires for n