正如我一直都明白的那样,主要案例在哪里 instanceof
适当的是:
- 实施
Object.equals(Object)
。所以,如果我正在写一个 List
上课,而不是延伸 AbstractList
无论出于何种原因,我都会实施 equals(o)
通过第一次测试 o instanceof List
,然后比较元素。
- 对特殊情况进行的重要(算法?)优化 不 改变语义,但只改变性能。例如,
Collections.binarySearch
做了 instanceof RandomAccess
测试,并使用略有不同的二进制搜索 RandomAccess
和非RandomAccess
名单。
我不认为 instanceof
代表这两种情况下的代码气味。但是有没有其他情况下使用它是明智的 instanceof
?
您控制之外的旧代码或API是合法的用例 instanceof
。 (即便如此,我宁愿在它上面写一个OO层,但时间有时会排除这样的重新设计。)
特别是,基于外部类层次结构的工厂似乎是常见的用法。
您控制之外的旧代码或API是合法的用例 instanceof
。 (即便如此,我宁愿在它上面写一个OO层,但时间有时会排除这样的重新设计。)
特别是,基于外部类层次结构的工厂似乎是常见的用法。
回答问题的一种方法是回答“Java库何时使用 instanceof
?“如果我们假设 番石榴 是一个设计良好的Java库的示例,我们可以看看它使用的位置 instanceof
决定何时可以接受。
如果我们提取Guava源代码jar并grep它,我们会看到 instanceof
被提及439次,涉及122个文件:
$ pwd
/tmp/guava-13.0.1-sources
$ grep -R 'instanceof' | wc -l
439
$ grep -Rl 'instanceof' | wc -l
122
看看其中一些案例我们可以看到几种模式出现:
检查是否平等
这是最常见的用法。这在某种程度上是特定于实现的,但假设您实际上想要根据它扩展/实现的类/接口来测量相等性,您可以使用 instanceof
确保您使用的对象是。但是,如果子类覆盖,这可能会导致奇怪的问题 equals()
而且不尊重同样的 instanceof
要求作为父母。各地的例子,但一个简单的例子 Lists.equalsImpl()
用于 ImmutableList
。
短路不必要的对象构造
您可以使用 instanceof
检查传入的参数是否可以安全地使用或返回而不进一步转换它,例如,如果它已经是所需类的实例,或者我们知道它是不可变的。参见中的示例 CharMatcher.forPredicate()
, Suppliers.memoize()
, ImmutableList.copyOf()
等
访问实现细节而不暴露不同的行为
这可以在Guava的所有地方看到,但特别是在静态实用程序类中 com.google.common.collect
包,例如在 Iterables.size()
它叫的地方 Collection.size()
如果可能,否则计算迭代器中的项目数 O(n)
时间。
避免打电话 toString()
我怀疑这个优点是在很少的情况下完成的,但假设你确定你做的是正确的事情,你可以避免不必要的转换 CharSequence
对象进入 String
s with instanceof
就像在做的那样 Joiner.toString(Object)
。
做复杂的异常处理
显然,“正确”的事情是使用a try/catch
阻止(虽然真的,那是在做 instanceof
检查已经),但有时你有更复杂的处理逻辑,值得使用条件块或将处理传递给单独的方法,例如拉出原因或具有特定于实现的处理。可以在中找到一个例子 SimpleTimeLimiter.throwCause()
。
看待这种行为突出的一件事就是几乎所有这些都在解决问题 我不应该解决。它们在库代码中很有用,例如在 Iterables
,但如果我正在实现这种行为,我应该问自己,是否有没有库或实用程序可以解决这个问题。
在所有情况下,我都会这么说 instanceof
检查应该只在内部用作实现细节 - 也就是说依赖于任何方法的调用者 instanceof
检查不应该(轻易)告诉你你做了什么。例如, ImmutableList.copyOf()
总是返回一个 ImmutableList
。作为它使用的实现细节 instanceof
避免构建新的 ImmutableList
s,但没有必要接受提供预期的行为。
顺便说一句,当我在挖掘Guava的源代码时,看到你的名字路易斯很有趣。我发誓我不知道!
你的第一个案例是我不会使用的例子 instanceof
运算符,但看看类是否相等:
o != null && o.getClass() == this.getClass()
这将避免一个实例 A extends B
和 B
被认为是平等的
我可以立即想到的其他情况,但我很确定有更多有效的案例
- 您拥有的工厂实例,例如a
canCreate
和 create
接收通用接口作为参数的方法。每个工厂都可以处理接口的特定实现,因此需要一个 instanceof
。仅定义工厂抽象类/接口中的接口允许例如编写复合工厂
- 复合实现(如我的第一个例子中所示)
正如你所提到的,“正确”的用途 instanceof
相当有限。据我所知,你基本上总结了两个主要用途。
但是,您可以稍微概括一下您的语句,如下所示:
- 在必要的演员表之前进行类型检查。
- 实现依赖于非常特定的类实例的特殊情况