I once had a colleague who had a higher rank than me, and who was not only unconvinced by my rational and articulate arguments for doing a certain thing in a certain way, but he concluded the discussion by stating that --and I am not paraphrasing here, this is what he actually said-- the way he wanted it done was mentioned in a book, so unless I could find a book backing up my proposal, it was to be done his way.
I did not argue with him at that time, (how can you argue with that?), but I would now like to quickly jot down my thoughts on why saying such a thing is incredibly stupid:
2015-04-30
Wow, 5 upvotes, 10 downvotes and counting
I have this answer on programmers.stackexchange.com which, at the time of writing these words, has 5 upvotes and 10 downvotes, and in all likelihood it will continue collecting downvotes, while I adamantly refuse to remove it, standing 100% by my ideas. I am dumbfounded, as such a thing has never happened before.
Here is the programmers.stackexchange.com question:
Here is the programmers.stackexchange.com question:
Is it okay to have objects that cast themselves, even if it pollutes the API of their subclasses?
I have a base class,Base
. It has two subclasses,Sub1
andSub2
. Each subclass has some additional methods. For example,Sub1
hasSandwich makeASandwich(Ingredients... ingredients)
, andSub2
hasboolean contactAliens(Frequency onFrequency)
.
Since these methods take different parameters and do entirely different things, they're completely incompatible, and I can't just use polymorphism to solve this problem.
Base
provides most of the functionality, and I have a large collection ofBase
objects. However, allBase
objects are either aSub1
or aSub2
, and sometimes I need to know which they are.
It seems like a bad idea to do the following:
for (Base base : bases) {
if (base instanceof Sub1) {
((Sub1) base).makeASandwich(getRandomIngredients());
// ... etc.
} else { // must be Sub2
((Sub2) base).contactAliens(getFrequency());
// ... etc.
}
}
So I came up with a strategy to avoid this without casting.Base
now has these methods:
boolean isSub1();
Sub1 asSub1();
Sub2 asSub2();
And of course,Sub1
implements these methods as
boolean isSub1() { return true; }
Sub1 asSub1(); { return this; }
Sub2 asSub2(); { throw new IllegalStateException(); }
AndSub2
implements them in the opposite way.
Unfortunately, nowSub1
andSub2
have these methods in their own API. So I can do this, for example, onSub1
.
/** no need to use this if object is known to be Sub1 */
@Deprecated
boolean isSub1() { return true; }
/** no need to use this if object is known to be Sub1 */
@Deprecated
Sub1 asSub1(); { return this; }
/** no need to use this if object is known to be Sub1 */
@Deprecated
Sub2 asSub2(); { throw new IllegalStateException(); }
This way, if the object is known to be only aBase
, these methods are un-deprecated, and can be used to "cast" itself to a different type so I can invoke the subclass's methods on it. This seems elegant to me in a way, but on the other hand, I'm kind of abusing Deprecated annotations as a way to "remove" methods from a class.
Since aSub1
instance really is a Base, it does make sense to use inheritance rather than encapsulation. Is what I'm doing good? Is there a better way to solve this problem?
Tags: java, inheritance, type-casting
asked by codebreaker
Subscribe to:
Posts (Atom)