Chris Uppal said:
DeMarcus said:
What's the common opinion about using the java keyword
instanceof?
I don't disagree with the previous replies that I've seen, but I wanted to give
a slightly different spin to the answer.
Think of it this way: the system (Java's semantics and OO in general) is
designed so that objects are responsible for their own behaviour. That's to
say that it's /their/ responsibility to know "what they are" and what they
should do in any specific circumstance. If you use instanceof then you are
cutting them out of the loop. In effect you are saying, "no, I don't care what
you think, I'm going to do this /my/ way".
Now that's fine if you know what you are doing and have a good reason. But you
are also throwing away the modularity, flexibility, comprehensibility, and
maintainability that were the point of using OO in the first place.
You can think of 'instanceof' as a specific kind of reflection. Other examples
of reflection are the facilities in java.lang.reflect for looking at what
methods and fields are available, and the use of finalize() to reflect on an
object's lifetime. These features allow you more power and control over the
way the system works, but at the cost that /you/ have taken over responsibility
for some aspect of the operation that the system would otherwise look after for
you. Reflection in general is something that "ordinary" programs don't, and
shouldn't, make much use of. (Although, as Chris Smith noted, it does depend
on how dynamic a programming environment / language you are working with[*])
In general, even if you do need to use reflection (and 'instanceof'
specifically) to make some design work, then it won't be as ad-hoc hacks
scattered randomly around the system. You'd be able to look at the design in
advance and say (after some thought) "we'll need to use reflection <here> and
<here>, there's no need for it anywhere else".
And I think that's the point: if you are just scattering 'instanceof' around in
an arbitrary way (if a reader can't look at what a method does and reliably
guess whether you'd use 'instanceof' before reading your code) then you are
almost certainly creating an ugly mess of hacks.
BTW, there's nothing /inherently/ wrong with slimy hacks. Sometimes you can
use a well-placed hack to gather all the ugliness of (part of) a system into
one place and hide it where it doesn't affect everything else. You'll be able
to recognise such uses quite easily since there will be a long comment
apologising for the hack, and explaining why it's better than the alternatives
in this case.
/snip/
I agree. Using "instanceof" is similar to using a switch block
with some kind of "type code" thing that distinguishes the actual
type. The general rule of OOP is that such constructs should be
replaced with polymorphism where possible. In the case of external
non-OO data, a switch block is necessary for creating the initial
object with an appropriate subtype.
I've used "instanceof" mostly in places where I was too lazy
to do it the right way. For example, an ArrayList that has both
JPanel and JLabel instances, I would use "instanceof" to distinguish
and downcast the next instance that I pulled from the ArrayList. It
was simple, it worked, and the ArrayList was very private. There was
no way that the "instanceof" usage could spread through-out the
application.
For a more flexible approach, I use something like this:
=========================================
public class Fubar
{
public void doGork(final Gork gork)
{
/* I want to do something with a Gork
that depends on the actual subtype
of Gork. */
gork.invoke(this);
}
public void doSomething(final Snafu snafu)
{
/* Snafu is a subtype of Gork. */
}
public void doSomething(final Gecko gecko)
{
/* Gecko is a subtype of Gork. */
}
}
public abstract class Gork
{
public abstract void invoke(Fubar fubar);
}
public class Snafu
extends Gork
{
public void invoke(final Fubar fubar)
{
/* calls Fubar.doSomething(Snafu) */
fubar.doSomething(this);
}
}
public class Gecko
extends Gork
{
public void invoke(final Fubar fubar)
{
/* calls Fubar.doSomething(Gecko) */
fubar.doSomething(this);
}
}
=========================================
This is a form of reflection that uses
overloading to distinguish the actual
subtype of Gork that was passed to
Fubar.doGork(Gork).
Since Fubar must know all of the subtypes of
Gorko (which it must know anyway when using
instanceof), this technique avoids instanceof
and achieves the same effect of distinguishing
the actual subtype of Gork on behalf of
Fubar.doGork(Gork).
A little imagination can extend this technique
to many subtypes of Gork by just adding the
appropriate overloaded "doSomething" method to
Fubar, and defining the new subtype of Gork with
the "invoke(Fubar)" method.
Notice that even though the "invoke" methods in
each of the subclasses are textually the same, their
effect is very different from each due to the usage
of "this" in the method call, which distinguishes
which overloaded "doSomething" method is called. You
would not want to refactor these methods to the parent
abstract class (do you see why?).
This is a very clean usage of polymorphism that
avoids "instanceof" and clearly separates the
work that Fubar.doGork(Gork) wants to perform on
the actual subtype of Gork.
--
----------------------------
Jeffrey D. Smith
Farsight Systems Corporation
24 BURLINGTON DRIVE
LONGMONT, CO 80501-6906
http://www.farsight-systems.com
z/Debug debugs your Systems/C programs running on IBM z/OS for FREE!