I mean, if I declare any access modifier in Java, that modifier can be circumvented (package-private and protected – package injection; any – reflection). I know that declaring this stuff helps to understand and organize the code, but besides that, are there any technological reasons or inherent Java functionality?
Thank you guys for wonderful response. I only regret I can tag only one answer as accepted.
Yes, they can be circumvented. The example of dependency injection which you pointed out is actually a useful case of circumvention. Injection to private members doesn’t pollute interfaces and isn’t easy to muddle with at run-time. However to see the core benefit, think of it like you’re a software architect presiding over a very lazy or inexperienced team.
Remember that well-factored objects should have high cohesion, and loose coupling. High cohesion means that an object deals with the concerns it’s semantically meant to deal with, nothing more, nothing less. Loose coupling means that objects depend on interfaces rather than specific implementations. Remember that the benefit to doing this is that it makes your code much easier to read, understand, test, and change. This reduces cost and in general makes stakeholders happy. As an architect, this is what you care about. The hypothetical lazy/inexperienced team in question only cares about immediately finishing whatever tasks are in their queue, even at the expense of quality and future productivity.
Access modifiers show some of the intent behind the interface defined for an object. Generally if you’re writing well-factored code, you almost always only have need for public and private modifiers. In the rare case where a concern can’t be easily/clearly separated into a single cohesive object, there’s also the “protected” keyword which allows limited external access from objects which should logically share the concern in question.
A better, though arguably more cumbersome, way of expressing the intent behind an interface is design by contract, which uses a much more robust set of interface annotations in order to allow preprocessing tools to enforce interface rules.
tl;dr:
Compiler enforcement of access modifiers is wonderful. The compiler puts up a pretty good barrier of inconvenience to those who would otherwise be happy reducing the cohesion and increasing the coupling of your codebase in order to cross off their immediate task quicker.