Louis Wasserman
Louis Wasserman

Reputation: 198014

When is using instanceof the right decision?

As I've always understood it, the main cases where an instanceof is appropriate are:

  1. Implementing 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.
  2. A significant (algorithmic?) optimization for a special case that does not change semantics, but only performance. For example, 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

Answers (4)

dimo414
dimo414

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 Strings 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 ImmutableLists, 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

Dave Newton
Dave Newton

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

Robin
Robin

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

  • factory instances where you have for example a 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 factory
  • composite implementations (as illustrated in my first example)

Upvotes: 4

tskuzzy
tskuzzy

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:

  1. Type-checking before necessary casts.
  2. Implementing special case scenarios that depend on very particular class instances

Upvotes: -1

Related Questions