16

As a core of Jigsaw project is the Java Module System, it would be nice to have an ability to restrict access to particular program elements (classes, methods and fields) within particular module only.

It can be helpful when there are some elements in module which are essentially public for this module, but shouldn't be accessible outside this module.

So I'm talking about the next level of access after "package-local", which could be named "module-local".

However a brief look on Jigsaw rules and early specs didn't help me to find out such kind of functionality. More specifically this Modifier specification doesn't contain any new elements.

So is there any other possibility to do it in future Java 9?

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • To my understanding modules are by default nailed shut. You have to explicitly export things to make them requirable from the outside. http://openjdk.java.net/projects/jigsaw/quick-start and https://en.wikipedia.org/wiki/Java_Module_System . Am I just misunderstanding what you're asking about here? – Gimby Aug 11 '16 at 13:37
  • According to [The State of the Module System](http://openjdk.java.net/projects/jigsaw/spec/sotms/) only packages that are exported are available outside the module. See [this table](http://stackoverflow.com/questions/215497/in-java-difference-between-default-public-protected-and-private/33627846#33627846) too for instance. – aioobe Aug 11 '16 at 13:41
  • @Gimby You are quite right: it is possible to export particular packages. But what about methods and fields? Yes, I think you understands my question quite right. – Andremoniy Aug 11 '16 at 13:42
  • @aioobe Yes, this is about packages. But it isn't very flexible way of declaring what exactly I want to export and what to leave hidden. What about fields & methods? – Andremoniy Aug 11 '16 at 13:43
  • 1
    The export mechanism works on package level I'm afraid. To quote sotms linked above: "Thus, even when a type is declared public, if its package is not exported in the declaration of its module then it will only be accessible to code in that module. A method or field referenced across module boundaries is accessible if its enclosing type is accessible, in this sense, and if the declaration of the member itself also allows access." – aioobe Aug 11 '16 at 13:43
  • @aioobe It looks like, yes. That is why I've raised this question. May be I've missed something, or it can be done in a slightly other way – Andremoniy Aug 11 '16 at 13:45
  • Cool, thanks :) I would like be there also to be able ask devs for such question and receive answers from primary sources :) I believe you could now provide fully credible answer to this question! – Andremoniy Aug 11 '16 at 14:22
  • 1
    I'd prefer to not write anything as it has not yet been released, and there's no way to tell whether the current functionality is what will be shipped. But you can follow the discussions on jigsaw-dev. See for instance [this thread](http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-November/005276.html). – aioobe Aug 11 '16 at 15:08
  • This is an old thread, but just for the record, sealed classes/interfaces introduced in JDK 15 fix the final issue. We can impose that a class/interface can be extended only by pre-defined classes/interfaces – Manoel Campos Jun 12 '21 at 10:48
  • About a new module-private modifier, isn't there anything on road map? – Manoel Campos Jun 12 '21 at 11:07

2 Answers2

16

A public element (i.e., a class, interface, method, or field) in a non-exported package is, in effect, “module local.” It will be accessible to all other code in the module, but not from outside the module.

There is no way to declare a module-local element in an exported package. A public element of an exported package is accessible from outside the module, a package-private element is still package-private, and there’s no element-level access mode between these two modes. We could define a new such mode but we’ve seen few compelling use cases for it and, moreover, implementing modular access control in the JVM at a granularity finer than that of exported packages would impose significant performance costs.

Mark Reinhold
  • 5,065
  • 1
  • 19
  • 16
  • 1
    "but we’ve seen few compelling use cases for it" -- not in the Java world, I'm sure. Have you looked elsewhere? Having built a non-trivial piece of Swift, to me the lack of `internal` defeats most of the potential advantages of modules regarding modeling/design. Our use case: a `User` "resource" that has cryptographic keys. While `User` is public, the keys should be module-private, but not necessarily package-private. I guess you'd have to work around by only exposing interfaces -- but then you'd have to deal with users potentially implementing them in ways you don't expect. – Raphael Oct 24 '17 at 11:09
  • Similar problems arise with subclassing. `final` prevents subclasses *everywhere*, so I can't declare a type that I can subclass within the module, but can be sure won't be subclassed outside of it. (Or can I?) – Raphael Oct 24 '17 at 11:12
  • "access control in the JVM at a granularity finer than that of exported packages" -- Aren't modules _coarser_ than packages? Have I misunderstood? – Raphael Oct 24 '17 at 11:12
  • 1
    Addendum: the lack of a module-private access level will mean that code will be distributed into packages based on visibility requirements, not on modelling considerations. I have a hard time understanding why that would be a good thing. – Raphael Oct 24 '17 at 11:49
8

Short Answer

It can be helpful when there are some elements in module which are essentially public for this module, but shouldn't be accessible outside this module.

That is not possible. (With means of the module system alone - there is a workaround.)

Long Answer

The explanation lies within the term Accessibility:

The Java compiler and virtual machine consider the public types in a package in one module to be accessible by code in some other module only when the first module is readable by the second module, in the sense defined above, and the first module exports that package. [...]

A type referenced across module boundaries that is not accessible in this way is unusable in the same way that a private method or field is unusable: Any attempt to use it will cause an error to be reported by the compiler, or an IllegalAccessError to be thrown by the Java virtual machine, or an IllegalAccessException to be thrown by the reflective run-time APIs. [...]

A method or field referenced across module boundaries is accessible if its enclosing type is accessible, in this sense, and if the declaration of the member itself also allows access.

While there are different ways exactly how and to whom a package can be exported, once the compiler/JVM deems a type accessible no additional mechanism applies. Its members are as accessible as they were before Jigsaw.

This means that there is no way to have an accessible type's members visible within the module (that would require public) but not outside of it (because a public member of an accessible type is accessible).

Workaround

So is any other possibility to do it in future Java 9?

Yes. :)

You can have a public interface Global in an exported package that defines the methods you want to export to the world. Then have either an interface or a class Local extend Global and add all the members you want. Key is that Local must not be in an exported package!

Now if your module's API only returns Global-s but never accepts them as a method argument, you're good to go. Just make sure that internally you always use - and maybe cast to - Local.

If you also accept Global-s you have to clearly document that these can only ever be instances your API returned (i.e. the user is not allowed to create her own implementation). This might sound prohibitive but if you think hard about your original request, it would have the same characteristics.

Nicolai Parlog
  • 47,972
  • 24
  • 125
  • 255
  • I'm facing same issue. As you mentioned, if methods in the internal API module just accept `Global` objects, I'll need to cast them to `Local` here and there. I don't like that because it's not clean and may require checking for type-safety (the user may create a new class from `Global` which doesn't implement `Local`). If I place all classes/interfaces from the internal module inside the same pkg and use pkg-visibility for some members, I can accept `Local` as parameters, solving the issue. However, I think it's horrible to put everything inside the same pkg. Any ideas? Thanks – Manoel Campos Jun 11 '21 at 12:27