Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 947
Louis Wasserman Avatar asked Jan 21 '12 23:01

Louis Wasserman


1 Answers

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!

like image 63
dimo414 Avatar answered Oct 03 '22 22:10

dimo414