What you are doing is perfectly legitimate. Do not pay attention to the naysayers who merely reiterate dogma because they read it in some books. Dogma has no place in engineering.
I have employed the same mechanism a couple of times, and I can say with confidence that the java runtime could have also done the same thing in at least one place that I can think of, thus improving performance, usability, and readability of code that uses it.
Take for example
java.lang.reflect.Member, which is the base of
java.lang.reflect.Method. (The actual hierarchy is a bit more complicated than that, but that's irrelevant.) Fields and methods are vastly different animals: one has a value that you can get or set, while the other has no such thing, but it can be invoked with a number of parameters and it may return a value. So, fields and methods are both members, but the things you can do with them are about as different from each other as making sandwiches vs. contacting aliens.
Now, when writing code that uses reflection we very often have a
Member in our hands, and we know that it is either a
Method or a
Field, (or, rarely, something else,) and yet we have to do all the tedious
instanceof to figure out precisely what it is and then we have to cast it to get a proper reference to it. (And this is not only tedious, but it also does not perform very well.) The
Method class could have very easily implemented the pattern that you are describing, thus making the life of thousands of programmers easier.
Of course, this technique is only viable in small, well-defined hierarchies of closely coupled classes that you have (and will always have) source-level control of: you don't want to be doing such a thing if your class hierarchy is liable to be extended by people who are not at liberty to refactor the base class.
Here is how what I have done differs from what you have done:
The base class provides a default implementation for the entire
asDerivedClass() family of methods, having each one of them return
Each derived class only overrides one of the
asDerivedClass() methods, returning
this instead of
null. It does not override any of the rest, nor does it want to to know anything about them. So, no
IllegalStateExceptions are thrown.
The base class also provides
final implementations for the entire
isDerivedClass() family of methods, coded as follows:
return asDerivedClass() != null; This way, the number of methods that need to be overriden by derived classes is minimized.
I have not been using
@Deprecated in this mechanism because I did not think of it. Now that you gave me the idea, I will put it to use, thanks!
C# has a related mechanism built-in via the use of the
as keyword. In C# you can say
DerivedClass derivedInstance = baseInstance as DerivedClass and you will get a reference to a
DerivedClass if your
baseInstance was of that class, or
null if it was not. This (theoretically) performs better than
is followed by cast, (
is is the admittedly better named C# keyword for
instanceof,) but the custom mechanism that we have been hand-crafting performs even better: the pair of
instanceof-and-cast operations of Java, as well as the
as operator of C# do not perform as fast as the single virtual method call of our custom approach.
I hereby put forth the proposition that this technique should be declared to be a pattern and that a nice name should be found for it.
Gee, thanks for the downvotes!
A summary of the controversy, to save you from the trouble of reading the comments:
People's objection appears to be that the original design was wrong, meaning that you should never have vastly different classes deriving from a common base class, or that even if you do, the code which uses such a hierarchy should never be in the position of having a base reference and needing to figure out the derived class. Therefore, they say, the self-casting mechanism proposed by this question and by my answer, which improves the use of the original design, should never have been necessary in the first place. (They don't really say anything about the self-casting mechanism itself, they only complain about the nature of designs that the mechanism is meant to be applied to.)
However, in the example above I have already shown that the creators of the java runtime did in fact choose precisely such a design for the
Method hierarchy, and in the comments below I also show that the creators of the C# runtime independently arrived at an equivalent design for the
MethodInfo hierarchy. So, these are two different real world scenarios which are sitting right under everyone's nose and which have demonstrably workable solutions using precisely such designs.
That's what all the following comments boil down to. The self-casting mechanism is hardly mentioned.
instanceof, in a way that requires lots of typing, is error-prone, and makes it hard to add more subclasses.
Sub2cannot be used interchangeably, then why do you treat them as such? Why not keep track of your 'sandwich-makers' and 'alien-contacters' separately?