9

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.

4 Answers 4

8

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.

3
  • 1
    While I admittedly don't entirely agree with you 100%, after thinking more about it and the language features that Java has I think I'm going with this as my selected answer in this case. In .NET land subnamespaces aren't uncommon, and I've seen them used a lot to organize parts of a library, which is what I was attempting to do. The more I think about it given the language features I have to work with the more I'm thinking I need to avoid subpackages entirely by pulling them out into their own well-defined packages. Thanks for the suggestion!
    – JosephRT
    Commented May 10, 2018 at 13:54
  • 2
    @JosephRT I'm not very familiar with C#, but something you might not be aware of is that there's really no difference between a package and a subpackage in Java. com.foo and com.foo.bar have no special relationship as far as the language is concerned. Commented May 10, 2018 at 15:47
  • Gotcha, figured that out after I found (this)[stackoverflow.com/questions/9249357/… question. It's been long enough since I've worked with Java that I had some wrong ideas in my head as to how packages work. Appreciate you pointing out that particular point.
    – JosephRT
    Commented May 11, 2018 at 15:27
5

This may not apply to you since I don't know if Android supports it, but the major feature of the Java 9 update is exactly about this problem.

The modules feature of Java 9 (known as Project Jigsaw) gives you the ability to add a metadata file to a .jar that (among other things) lists the packages that are publicly visible. All other packages are internal to the module. In other words, public classes inside an internal module are equivalent to internal classes in a .Net assembly.

1
  • 1
    Unfortunately, this doesn't help with this specific case, but thanks for the info! I'd vaguely heard of Java modules but hadn't looked into them yet. I wasn't limited to Java 8 due to Android this is probably exactly what I needed. Thanks again! I'm going to dig into modules for future projects.
    – JosephRT
    Commented May 9, 2018 at 13:57
3

I agree with the other answers here, that the standard Java way is to not create the extra packages or use a package call internal. I did want to point out another option you have with Android.

Android's has a support annotation, RestrictTo. Anyone using the annotated code outside the proper scope gets a lint warning.

https://developer.android.com/reference/android/support/annotation/RestrictTo

This would allow you to annotate classes, methods, or fields as being restricted to the library itself.

@RestrictTo(RestrictTo.Scope.LIBRARY)

There's other options for the scope also. This is the pattern Google has been following with the support library.

1
  • Oh nice, I didn't know about that annotation, thank you! I'm going to give that a look and probably get to using it. I am trying to keep a decent amount of my functionality as pure Java to avoid Android dependencies, but this is still really useful. Thanks for letting me know about it!
    – JosephRT
    Commented May 26, 2018 at 22:10
1

“But possibly organized by subdirectories” makes no sense, as each subdirectory is a new package in Java.

What’s typically done is document what the public interface is by package, and document implementation classes as being open to change, use these directly at your own risk.

One way you could also use, and I’ve seen this used effectively, is split the library into 2 jar files. One file containing the interfaces for the public API only, and a second containing everything else. Provide ample documentation for the interfaces, and next to none for the implementation classes, in the version you release to the public.

And place some trust in your users. Trust them to stick to your public API, most will if it’s well enough designed there’s no need to mess with the internals directly, simply because messing with your internals would make their own work that much more brittle.

1
  • Fair point on subdirectories, I was thinking that the package structure and directory structure didn't have to line up one-to-one, but I think I'm mistaken. I would argue that having classes or interfaces public and simply not documenting them is the leaky abstraction that I'm attempting to avoid, though, to prevent both confusion and misuse. If someone is using my library I'd strongly prefer them not to have to think about whether what their IDE suggested was part of the public interface or the internals.
    – JosephRT
    Commented May 9, 2018 at 14:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.