Oracle Blog

On the design and specification of Java

Module membership declarations

With my JSR 294 spec lead hat on, I recently proposed a change to the superpackage model which JSR 294 defines in the service of JSR 277's deployment modules. Early feedback has been positive, but where to declare module membership in source code is an ongoing issue.

When module membership is decentralized, i.e. 'module M;' appears in compilation units, each compilation unit that declares 'package P;' can declare a different module. But a package must not be "split" among different modules. If two types in the same package could be in different modules, then each type could access the other's package-private members but not its module-private members. This is stupid; types in different packages in a module can access each others' module-private data so types in the same package ought always to be able to access each others' module-private data. For analogous reasons, deployment module systems frown on split packages too.

The question for JSR 294 is how to enforce consistent module membership across all the compilation units which declare a given package. I could just write a declarative statement in the JLS and let javac worry about enforcing it:

But making javac inspect every source/class file in a package whenever a compilation unit in that package is recompiled will hardly be popular. Instead, a common idea is to declare module membership in package-info.java:

// P/package-info.java
@Imports(...)
module M;
@Foo
package P;

Module membership is now not declared near a type declaration, but it's clear from the presence of a 'module' modifier in a compilation unit that a predictable package-info file should be consulted by the compiler or developer. And there's no issue with types in an unnamed package (i.e. no package-info), since they can never be module members anyway.

However, things aren't as neat as they look. The moral argument against using package-info is that an artifact in a classfile, such as module membership, should have a direct representation in the corresponding source file. This is the kind of argument you think you can ignore in the short term but that you rue ignoring in the long term. The other argument against package-info is that you should not actually annotate a module declaration there even though it feels natural to do so. Enumerating module-level annotations is important for JSR 277, and the right solution is to declare them in a module-info.java file, following the precedent of package-info.java. So now you have module-info which says 'module M;' and package-infos which say 'module M;' and maybe we should just centralize the module's package list in module-info and leave package-info and normal source/class files alone? Sadly this doesn't work because the compiler and developer have no way of easily finding the "correct" module-info file for a given package.

In summary, when viewing a module bottom-up (compiler or developer reading a single source/class file), module membership should be in package-info for convenience; when viewing a module top-down (277 tool packaging a module given its name and constituent classfiles), module attributes should be in module-info for completeness. The moral argument, that module membership should be in normal compilation units for clarity, loses. Both module-info and package-info should be able to say 'module M;', just as compilation units and package-info can say 'package P;'. Annotations on 'module M;' in a package-info file are strongly discouraged.

The superpackage model seams simpler with your proposed changes.
I like the fact a .class is completely self-described. So i vote yes (even if there is no poll :)

About module-info.java,
please don't use annotations to define imports (versions etc) please use real keywords no matter if they doesn't already exist.
Annotations must not change the semantics of a program.

Replacing any ACC_PUBLIC with ACC_MODULE will break codes that relies on reflection or bytecode manipulations. Are types with a module access tagged with ACC_PUBLIC and ACC_MODULE ?

Where module-info.java is defined on disk ?

Export list can be optional, it should be a way to specify it (like a constructor if there is no export list, create it).
Export list is a security feature, it avoids a class that is set public by mistake.
And let your favorite IDE to take care about how to specify it using regexes
or automagically.
Moreover, is it possible to do the same trick for imported types ?

Why don't you stop tinkering with Java (the language) and go with a proven solution: JSR-291 (aka. OSGi) is clean and easy.

No need to hamper the Java language with another bolt on solution to solve long standing issues without doing a proper re-evaluation of Java as a whole. Java stood once for an easy to learn, easy to use language. Alas, without proper spring cleaning, this language will reach the "COBOL stage" of computer languages much faster than any other language did before..

Would there be any problem in permitting (but not necessarily enforcing) a module declaration in a compilation unit, and, when present, enforcing that it is the same as the module declaration in the corresponding package-info? This enables programmers to comply with the moral argument while still keeping the definitive module declaration in the package-info where it belongs.

Also, why say "Annotations on 'module M;' in a package-info file are strongly discouraged.", when you could make it illegal? Is this another case where the "moral" argument gives the wrong answer?

@Remi: I don't want to complicate the core language with module system concepts like import dependencies, versioning, validation, and sharing. Different module systems have different realizations of those concepts, so javac can't possibly be responsible for controlling them. Annotations are the right way out, and an advisory export list is a good idea for a 277-compliant module builder to enforce (in cases types are accidentally public, as you say). On the topic of ACC_MODULE, yes, that will be visible through reflection on a classfile, and changing accessibility from public to module-private is binary incompatible.

@Bruce: A module declaration in a compilation unit as well as package-info is a good idea. "discouraged" will almost certainly become "It is a compile-time error if..." in the JLS.