It is stunning to see how many programmers simply ignore valuable language features like access modifiers. That's a pity. It takes some time to get familiar with them, but it pays, for sure.
The Java access modifiers are, in order of their encapsulation strength, descending, following reserved words:
private
- using no modifier is called "default access", or "package-visible"
protected
public
They can be mixed with two more keywords that define the nature of a field, method or class more exactly:
final
static
Ignoring access modifiers means writing code that lacks robustness. You must make clear what of the class is public, what is to be inherited only, and what must not be seen from outside at all. Thus access modifiers should be means of expression of your daily programming work. Encapsulation is the target. And the target of encapsulation is to reduce complexity by exposing just what's needed to the outer world. Complexity is what makes our life hard.
You might use an IDE that provides the auto-complete action Ctrl-Space. This enables fast and safe programming, without reading thick books before using an API. When the authors of that API used access modifiers in a correct way, you will, in your auto-complete, see just methods that really can be called or overridden, not all the confusing internals. Thus access modifiers are a prerequisite for a good auto-complete functionality.
Any modern programming language must provide some means for encapsulation, else you wouldn't be able to implement big applications with it.
Private
If I would have designed the Java language, I would have made private
the default access modifier.
Stating nothing about access (default access) means "stay in context", and the context is mostly the class, not the package.
Make as much as possible private
in your application, that way you hide complexity from the outer world.
Private
methods and fields can not be accessed by other classes, not even by sub-classes.
When having a hierarchy of classes, e.g. Animal
, Mammal extends Animal
, Cat extends Mammal
,
each of these could have a private move()
method implementation of its own, not overriding the super-class implementation.
A private
thing is usable only within its own class.
The private
modifier can be used on methods, fields and inner classes, but not on top-level classes.
Final
As this modifier plays together with all of the following, I describe it here and now.
A final
field can not be modified any more after it got its value once.
It is a constant, or an immutable field.
Such fields must get their value directly at definition, in a static- or instance-initializer, or in a constructor.
Try to use final
on fields as much as possible, it will make your implementation more robust and easier to understand.
Methods that are final
can not be overridden.
Private methods need not to be set final
, because they can not be overridden by nature.
Referring to the anti-pattern
DoNotCallOverridableMethodsInConstructor
I would recommend to make final
all methods that are called from a constructor.
Classes that are final
can not be derived (extended to sub-classes).
This is very useful for utility classes that contain just stateless static
methods.
Additionally declare a private
constructor on them and nobody will be able to wrongly instantiate such a class.
(No Modifier)
The so-called default-access expresses "package-visibility". A method or field with no access modifier can be read and written by classes in same package only. It is not visible outside the package, even sub-classes can not see it when they are in a different package.
This access modifier is rarely used, but it makes sense when you want to encapsulate several top-level classes into a package, instead of using inner classes (because the outer class would get too big then). Typically you would have some package-visible classes, and one public facade-class that uses all of them.
Default-access can be used on methods, fields, inner classes and top-level classes.
Protected
This is for inheriting to sub-classes.
In other words, you do not want that to be accessible by any class except a sub-classes.
Unfortunately the Java protected
modifier also allows read / write access by all classes in same package,
but this mostly is not a problem.
While the final
keyword does not make sense with the private
modifier (is final automatically),
it makes a lot of sense with protected
methods. You should distinguish between protected
methods
that are made to be overridden, and such ones that are made to be called by sub-classes (utility methods).
The last should be final
.
This makes life easier for developers that derive the class and wonder what they need to override now.
The protected
modifier can be used on methods, fields and inner classes, but not on top-level classes.
I would not recommend to make fields protected
, you lose their encapsulation to sub-classes.
Public
Visible for the whole world. Fields that are public
can be modified by everyone (avoid them).
Methods that are public
can be called by everyone, which may be what you want.
Make them final
when their overriding could break the "state" of the class.
Classes that are public
can be used by everyone, although you can restrict their instantiation
by the access modifiers of their constructors.
Try to avoid public
as much as possible.
If all methods of a class are public
, you missed encapsulation.
Exceptions prove the rule,
so anemic classes may have public
on all methods.
The public
modifier can be used on methods, fields, inner classes and top-level classes.
Mind that you should never have public
fields that are not final
.
Mutable non-final
fields make up the "state" of an object, and thus are primary subjects for encapsulation.
But public final
fields are OK. You don't need to write getter-methods then.
Just keep in mind that some
reflection utilities might not be happy with that.
Static
This modifier binds to the class, not to the object-instance (created from that class).
Fields that are static
are the same for all instances of a class.
Always make them final
.
A static final
field is what is called constant in other programming languages.
Code in static
methods is not reusable and overridable. Try to avoid that.
Static
methods always should be stateless, meaning the only fields they are allowed to use are their parameters.
Instances of static
inner classes are not bound to an instance of the outer class.
In other words, they do not have an implicit pointer to the enclosing object,
like instances of non-static
inner classes have.
This may be a disadvantage in some cases, but in general it makes
garbage collection easier,
and that pointer to the outer instance may not be needed at all in many cases.
So static
inner classes are the preferable ones.
The static
modifier makes no sense on top-level classes.
Shadowing
What happens when you have a class Animal
, and a class Mammal extends Animal
,
and both define a protected
field with name "mouth"?
This is an ambiguity that you should avoid. Always make fields private. If sub-classes need to see them,
create protected
getter-methods for them, and try to avoid the parallel setter.
Fields that are protected
and not final
can quickly get out of control.
My Recommendations
- Try to minimize visibility by selectively using access modifiers; hiding complexity means preventing its abuse.
- Fields always should be
private
; in any other case (default access,protected
,public
) they should befinal
. - Make all non-
private
methods called from a constructorfinal
, to avoid initialization bugs from overrides. - Use default-access as much as possible for hiding things within the package that the outer world doesn't need to see.
- Distinguish between
protected
methods that are to be overridden, and such that should be just callable by sub-classes (make the latterfinal
). - Avoid
static
methods, their code is not reusable in an object-oriented way; consider using singletons instead. - Prefer
static
inner classes, they don't have possibly unneeded dependencies to the enclosing object. - Make classes that contain just
static
stateless utility methodsfinal
, and define aprivate
do-nothing constructor on them; this makes a wrong instantiation impossible.
Keine Kommentare:
Kommentar veröffentlichen