Java’s default access
Within my team, we like to challenge each other, question things and enforce a pragmatic, hands-on approach. Therefore, it was no surprise the following conversation started on Slack:
Just a bit side-off discussion — what kind of benefits does the package-private give? I mean more specific than “better encapsulation”. More like “what kind of problems you faced before that this strategy would mitigate/solve”?
I probably wrote too much, as teammates suggested converting it into a blog post…
Modularity
Why do we use private
fields and methods when everything can be just public
? List all the pros and most (if not all) of them would work at the package level. Then, at the module level, at the microservice level, etc.
High cohesion & encapsulation to put it simply. But it’s not all.
It’s all about making better abstractions and a more dev-friendly codebase. Reading code is more frequent than actual coding and it’s useful to have something like a table of contents to quickly verify if you look at the part that is worth reading at the moment. I see a proper usage of packages and package-private access as that.
Also, in that limited situations when you actually write code (😉), having much lower number of available classes/methods to choose from, makes it easier to choose a proper one. Less erroneous, and less likely to misuse that part of the codebase.
Enforcing package-by-feature approach
Still one can say, the world didn’t collapse because of not using package-private access. I think the same can be said about many other things, but is that our only metric?
How about organizing code using package-by-feature approach? It’s not as extreme as with package-private access, but I think most code I’ve seen was organized rather with package-by-type (a.k.a. package-by-layer) approach. Especially when counting in front-end codebases I worked with. Most code is organized like that, but it doesn’t mean it’s easier to work with such a code. See nice, thought-provoking examples at the end of this article: http://www.javapractices.com/topic/TopicAction.do?Id=205.
I think it’s obvious, but let me emphasize it
- Package-by-feature helps to have package-private access
- Using package-private access makes you think more in package-by-feature manner
- Contrary, package-by-layer forces you to use
public
access to make classes from different packages visible to each other
Default
There is another reason people don’t use package-private access as much as they could. It was meant to be default access (Java design), but then IDEs and alternative JVM languages made public
a default. My (educated) guess is they went that way because package-by-layer approach was encouraged by Java EE, Grails and other frameworks.
Taking a wonderful IntelliJ as an example, you have 3 places to override settings to encourage package-private access:
- Check Show Visibility Icons in Project Window -> Tree Appearance
- Modify File and Code Templates under Preferences
- Check a proper option under Code Style -> Java -> Code Generation
Because public
is effectively a default, it’s in many articles, code examples, open source projects, etc. So fewer people think package-private access is useful, etc. And it drives itself.
Nevertheless, the original idea was to have a different default 🙂
Join the elite 😉
Despite the above, frameworks like Spring and JUnit 5 (fun fact: version 5 premiered in 2017) worked with Java’s default from the very beginning. So it’s not like no one was thinking about the package-private access. I’d even call these two Top Performers and I prefer taking the inspiration from them 😉
Implementing well-known patterns
There is a huge library of software design/integration/architecture patterns. You might not like package-private access, but willing to use e.g. Service Layer. My recommendation is to treat package-private access as a more convenient way of introducing the pattern. Treat it as an implementation detail and a helping technique.
Smarter code organization
One can still say packages should be used to organize the code and push towards package-by-feature approach and having 20+ package-private classes and one public
Service or Facade class might not be the ideal organization.
My response is with Java’s default access as a default option you still use (sub)packages as the organizing structure, but you use them smarter 😄
- REST API and DTOs can go to a separate subpackage. It’s even better because you ensure
RestController
doesn’t expose anything “internal” from the part of the system - Considering CQRS, a whole Query part can be a dedicated subpackage, containing just
public
classes - The whole infrastructure part created with Spring can be a dedicated subpackage (Spring works perfectly with package-private access, remember? 🙂)
- Design patterns requiring class hierarchies can have their dedicated subpackages. E.g.
strategy
subpackage with apublic
interface and all package-private implementations
Thanks to promoting package-private access, you also discover files as a way of organizing your code. Something between the class and the package. I’d say it’s even encouraged with Java 16+ sealed
classes, which can skip permits
for same-file implementations.
Apart from files, I observed I started using Maven/Gradle modules better. E.g. you can extract adapters
or api
modules and use the same packages between them. So effectively you have like 20+ classes under a package but split into 2+ modules.
Real-life benefits
There are people much smarter than I who promote package-private access. I recommend taking a look at J. Bloch, Effective Java. Precisely, Item 15: Minimize the accessibility of classes and members. I also recommend watching
And my personal experiences connected with the above:
1. Not only I watched that talk 20+ times, but I also worked with the presented code for a moment, so I have some insight knowledge 😄
- It was something called “team tourism” at the company I worked and the presenter was on vacation when I worked with his team
- System was developed for a couple of years already and I was able to introduce a new, quite serious feature within a week. I mean from 0 to deploy it on production. I started working on Monday and the next Monday I got my first feedback from the end users (it was a back-office app)
- There was a new team member there as well and he introduced a change, making one thing
public
. It was immediately caught by reviewers and comments were not like “rename this method”, but rather “is this a responsibility of that module”, “should this module know about that/expose that”, etc. Modularity was just a part of the team’s DNA. I see this as a side-effect of design, because looking at the team seniority, etc., it wasn’t a very experienced team
2. Closer to what is highlighted by Joshua Bloch — I guess you faced API versioning problems, didn’t you? When you have REST/events and there are already clients/other systems using them, you need to support old versions for almost forever 😄
- In one team we decided to introduce service clients rather than public API. Then, package-private access was really handy to hide things. Similarly to what you do with limiting your REST services, but at the code level. I also used service clients later, in another company, and just interfaces were left
public
there - I also have some experience with autoconfigs, libraries and software plugins. Package-private access really makes these things easier to maintain. Contrary, what’s
public
has to be treated as “published” there
TL;DR
Modularity, ownership, security/confidence and freedom to change 😄