Reputation: 198014
As I've always understood it, the main cases where an instanceof
is appropriate are:
Object.equals(Object)
. So if I were writing a List
class, and not extending AbstractList
for whatever reason, I would implement equals(o)
by first testing o instanceof List
, and then comparing elements.Collections.binarySearch
does an instanceof RandomAccess
test, and uses a slightly different binary search for RandomAccess
and non-RandomAccess
lists.I don't think instanceof
represents a code smell in these two cases. But are there any other cases where it is sensible to use instanceof
?
Upvotes: 12
Views: 2235
Reputation: 48794
One way to answer your question would be to answer "when does a solid Java library use instanceof
?" If we assume Guava is an example of a well designed Java library, we can look at where it uses instanceof
to decide when it is acceptable to do.
If we extract the Guava source code jar and grep it, we see instanceof
is mentioned 439 times, across 122 files:
$ pwd
/tmp/guava-13.0.1-sources
$ grep -R 'instanceof' | wc -l
439
$ grep -Rl 'instanceof' | wc -l
122
And looking at some of these cases we can see several patterns emerge:
To check for equality
This is the most common usage. This is somewhat implementation specific, but assuming you do in fact want to measure equality based on the class/interface it extends/implements, you can use instanceof
to ensure the object your working with is . However this can potentially cause odd problems if a child class overrides equals()
and doesn't respect the same instanceof
requirements as the parent. Examples everywhere, but an easy one is Lists.equalsImpl()
which is used by ImmutableList
.
To short-circuit unnecessary object construction
You can use instanceof
to check if the passed in argument can be safely used or returned without further transforming it, for instance if it's already an instance of the desired class, or if we know it's immutable. See examples in CharMatcher.forPredicate()
, Suppliers.memoize()
, ImmutableList.copyOf()
, etc.
To access implementation-details without exposing different behavior
This can be seen all over the place in Guava, but notably in the static utility classes in the com.google.common.collect
package, for instance in Iterables.size()
where it calls Collection.size()
if possible, and otherwise counts the number of items in the iterator in O(n)
time.
To avoid calling toString()
I'm skeptical this merits being done in more than a very select few cases, but assuming you're sure you're doing the right thing, you can avoid needlessly converting CharSequence
objects into String
s with instanceof
, like is done in Joiner.toString(Object)
.
To do complex Exception handling
Obviously the "right" thing to do is use a try/catch
block (though really, that's doing instanceof
checks already), but sometimes you have more complex handling logic that merits using conditional blocks or passing processing off to a separate method, for instance pulling out causes or having implementation-specific processing. An example can be found in SimpleTimeLimiter.throwCause()
.
One thing that stands out looking at this behavior is nearly all of them are addressing problems I should not be solving. They're useful in library code, e.g. in Iterables
, but if I'm implementing this behavior, I should probably be asking myself if there aren't libraries or utilities that solve this for me.
In all cases, I would say that instanceof
checks should only ever be used internally as an implementation detail - that is to say the caller of any method that relies on an instanceof
check should not be able to (easily) tell that's what you did. For instance, ImmutableList.copyOf()
always returns an ImmutableList
. As an implementation detail it uses instanceof
to avoid constructing new ImmutableList
s, but that is not necessary to acceptably provide the expected behavior.
As an aside, it was amusing coming across your name, Louis, as I was digging through Guava's source code. I swear I had no idea!
Upvotes: 7
Reputation: 160170
Legacy code or APIs outside of your control are a legitimate use-case for instanceof
. (Even then I'd rather write an OO layer over it, but timing sometimes precludes a redesign like that.)
In particular, factories based on external class hierarchies seem a common usage.
Upvotes: 6
Reputation: 36611
Your first case is an example where I would not use the instanceof
operator, but see whether the classes are equal:
o != null && o.getClass() == this.getClass()
This will avoid that an instance of A extends B
and B
are considered equal
Other cases I can immediately think of but I am pretty sure more valid cases are available
canCreate
and create
method which receive a general interface as parameter. Each of the factories can handle a specific implementation of the interface, so it would require an instanceof
. Defining only the interface in the factory abstract class/interface allows for example to write a composite factoryUpvotes: 4
Reputation: 36446
As you have mentioned, the "correct" uses of instanceof
are rather limited. As far as I know, you have basically summed up the two main uses.
However you can generalize your statements a bit though as follows:
Upvotes: -1