Minimizing Java Library’s Exposed Interface with Subpackages

api-designjavalibraries

I'm making a library or two for and Android app and I want to keep the library's exposed interfaces to a minimum to avoid the abstraction leaking everywhere by avoiding making all of the classes 'public'. I'm also following Maven's package structure.

Coming from C#, I've used the internal access modifier to accomplish what I want; if you're not familiar, internal grants access to anything within the assembly, but not outside of it. So the few public interfaces are truly public. The closest analogy, in my opinion, is package-private in Java, but that doesn't quite work when I organize my library into subpackages.

For example, say I have a class Service1 in root and another class Service2 in a subpackage under root. I'd like Service1 to be able to reference Service2's interface, but preferably not make Service2 public as that'd expose Service2's interface outside of the package, which isn't what I intend.

I imagine this isn't terribly complicated, but I'm a little bit confused as to how to allow sibling/parent packages to access subpackage classes without making them public. The only solution I've been able to really think of is to either 1) put everything at the same package level which would allow me to use package-private to hide everything that shouldn't be publicly available but is extremely messy or 2) suck it up and make my interfaces public, which admittedly offends my sensibilities.

How is this normally done? Are all the classes in the same package, but possibly organized by subdirectories? I've yet to come up with an approach that I truly like.

Best Answer

In Java, your intended API surface should define your package structure. Avoid the temptation to create subpackages just to "organize" your files in a way that doesn't directly relate to your API design. The perceived need to do so may even hint at poor API design.

With that in mind, you may still want to use a subpackage to create an internal API. For example, a group of classes might want certain members to be accessible to other classes in the group, but not expose those members to the rest of your library. In that case, put them in a subpackage and take advantage of package-private access. You'll need to think carefully about whether the exposed API of the subpackage is something you want to design, document, and support as a separate product. Doing so will make your product more valuable, but of course at some cost to you. If you don't want to support the internal API, or you're not sure yet, the usual solution is to put that API in a subpackage with a name that identifies it as internal, like com.myproduct.internal, or even an entirely different package hierarchy, like the com.sun classes that are exposed by necessity but officially not part of the standard Java API.

Related Topic