|
|
|
Sponsored Link •
|
|
Advertisement
|
Bill Venners: In Effective Java, Bloch offered a
good example in which the Bloch suggests that the addAll method in class
HashSet calls add on itself. A subclass attempts to track
the total number of objects added to the set by counting the objects passed to both
addAll and add, but it doesn't work as expected because
of addAll's self-use. If someone passes three objects to
addAll, the total number of objects increments by six, not three, because
addAll calls add three times.
addAll method should document its use of the
add method. Therefore, designing for inheritance presents the problem: if
I document a method's self-use, then that method must always use itself in that way.
Documenting self-use restricts future implementation changes.
Ken Arnold: You can say the method has the option to do something without saying it will definitely do it, in some cases. But in other cases, you want to say: When I need a certain piece of internal data, I will call this method to get it. You want to document that, because you want subclasses to override the method and return a subtype of what you expect. That method's purpose is enabling subclasses to return subtypes. But, again, that is a matter of protected methods.
You certainly don't want to do things that set up sharp edges for subclassers. Yes, you
can set things up so that if someone overrides something, an unexpected event occurs.
Say you are the designer and implementer of a class, and at some point in the code you
call the method foo. If you are unpleasantly surprised when a subclass
overrides foo and does something unexpected, it seems to me there are
two possibilities.
The first possibility is that the person who subclassed foo didn't obey
foo's contract. Instead, he overrode foo and did something
wrong; in which case, that is the subclasser's problem. He shouldn't override
equals to return true only if the square root of one is the same as the other.
That is just not what the equals method means. You can't prevent people
from making that kind of mistake, so it is not your fault.
The other possibility is that your use of foo isn't properly defined in
its contract. You might be using foo in a way that relies on something
about your internal details that you don't describe, so other people interpret it incorrectly.
In principle, I can see that kind of problem happening. But in either case, one party or the
other has done something wrong.
This will also happen when methods are called during the construction phase. From the constructor, you are in a state where various things haven't been initialized. So you probably have to note which methods the class calls on itself during construction, because the methods have to be prepared for the list never having been created as opposed to having assumed it was created.
Bill Venners: Or as you said, saying that these methods may be called by the constructor, so subclassers need to make sure calling the methods during construction will work.
Ken Arnold: Or the constructor needs to make sure that the normal preconditions for calling are, in fact, set up. Although you can't do that with the subclass's own data, which is where things get really complex.
|
Sponsored Links
|