There's just one thing I would like to add to the accepted answer, because I don't entirely agree with his conclusion.
We've all done this.
class Engine {
public Engine() {
init();
}
void init() {
lockDoors();
releasePressure();
tightenSeatbelts();
launchRocket();
}
...
}
Now the question is, which access modifier should we add to our init()
function. Should it be private or protected.
- make it private <-- keeps subclasses out
- make it protected <-- allows subclasses in
Before you make a choice
Now first of all, you should realize that (almost) all code in the Engine
class can be replaced by a subclass.
- code in a public function, can easily be overridden
- code in a protected function, can easily be overridden
- code in a private function, can be replaced by overriding all methods that call it.
Well, there is just one exception:
- you can never modify the code of a constructor
- you can never avoid a private method being called from the constructor of a super class.
- (and of course, you cannot replace a
final
method)
Protected init() - the wrong way
Let's say the init()
method is protected
there is indeed a pitfall. It is tempting to override it to add features as follows. That would indeed be a mistake.
class SubEngine extends Engine {
int screws = 5;
void init() {
tightenScrews();
super.init();
}
void tightenScrews() {
// this won't print 5, but it will print 0.
System.out.println("tightening " + screws + " screws");
}
}
Protected init() - the right way
So, basically, you should just disable the parents code and postpone execution to your own constructor instead.
class SubEngine extends Engine {
int screws = 5;
public SubEngine() {
initSubEngine();
}
void init() {
// disable parent code
}
void initSubEngine() {
tightenScrews();
super.init();
}
void tightenScrews() {
// this will print 5 as expected
System.out.println("tightening " + screws + " screws");
}
}
Private init() - you may need a phonecall
Now, what if the init()
method is private
?
- Like mentioned above, there is no way to disable the code of a parent constructor. And if init() is private you simply cannot disable it.
- You'll end up copying the entire
Engine
class, perhaps just to add 1 line of code.
- And that may not be the end of it. Even after copying your class, your copied object won't be an
Engine
meaning that you won't be able to use your EngineUtil#inspectEngine(Engine engine)
function.
- Perhaps somebody knew this in advance and made an
IEngine
interface. Then you can get away with it.
- In practice it means you'll have to take your phone, and call to that other department that made the
Engine
class, and ask them to change their code a little to take away some restrictions.
Intelligent design
There is another way. Constructors are for setting variables. They shouldn't activate anything. Everytime you see a class creating a new Thread
from their constructor (or through a private method) that should be a red flag.
class Engine {
public Engine() {
}
public void init() {
lockDoors();
releasePressure();
tightenSeatbelts();
launchRocket();
}
// and you probably also want one of these
public void shutdown() { ... }
...
}
Intention
Of course, your intention may very well be not to open up your code. Perhaps you really don't want to allow others to extend your classes. There certainly can be cases where you want to lock people out.
Be aware that it will also make it harder to write tests for your code.
Anyway that's a different scenario.