什么时候使用instanceof正确的决定?

时间:2012-01-21 23:50:07

标签: java design-patterns

正如我一直理解的那样,instanceof合适的主要案例是:

  1. 实施Object.equals(Object)。因此,如果我正在撰写List课程,而不是出于任何原因而延伸AbstractList,我会首先测试equals(o),然后比较元素来实施o instanceof List
  2. 对于改变语义但仅改变性能的特殊情况的重要(算法?)优化。例如,Collections.binarySearch执行instanceof RandomAccess测试,并对RandomAccess和非RandomAccess列表使用稍有不同的二进制搜索。
  3. 我不认为instanceof代表这两种情况下的代码气味。但是有没有其他情况下使用instanceof是明智的吗?

4 个答案:

答案 0 :(得分:6)

您控制范围之外的旧版代码或API是instanceof的合法用例。 (即便如此,我宁愿在它上面写一个OO层,但时间有时会排除这样的重新设计。)

特别是,基于外部类层次结构的工厂似乎是常见用法。

答案 1 :(得分:6)

回答问题的一种方法是回答“实体Java库何时使用instanceof?”如果我们假设Guava是一个设计良好的Java库的示例,我们可以查看它使用instanceof的位置来决定何时可以接受。

如果我们提取Guava源代码jar并grep它,我们会看到instanceof在122个文件中被提及439次:

$ 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。{/ p>

  • 短路不必要的对象构建

    您可以使用instanceof检查传入的参数是否可以安全使用或返回而无需进一步转换它,例如,如果它已经是所需类的实例,或者我们知道它是不可变的。请参阅CharMatcher.forPredicate()Suppliers.memoize()ImmutableList.copyOf()等中的示例。

  • 访问实施细节,而不会暴露不同的行为

    这可以在Guava的所有地方看到,但特别是在com.google.common.collect包中的静态实用程序类中,例如在Iterables.size()中,如果可能的话,它会调用Collection.size(),并且否则在O(n)时间内计算迭代器中的项目数。

  • 避免致电toString()

    我怀疑这个优点是在非常少的情况下完成的,但假设你确定你做的是正确的事情,你可以避免不必要地将CharSequence个对象转换为{{1} Stringinstanceof相同。

  • 执行复杂的异常处理

    显然,“正确”的事情是使用Joiner.toString(Object)块(尽管真的,那已经在进行try/catch检查),但有时你有更复杂的处理逻辑,值得使用条件块或传递处理单独的方法,例如拉出原因或具有特定于实现的处理。可以在instanceof

  • 中找到一个示例

有一点可以看出这种行为,几乎所有这些都在解决问题我不应该解决。它们在库代码中很有用,例如在SimpleTimeLimiter.throwCause(),但如果我正在实现这种行为,我应该问自己是否没有库或实用程序可以解决这个问题。

在所有情况下,我都会说Iterables检查只能在内部用作实现细节 - 也就是说任何依赖于instanceof检查的方法的调用者都不应该能够(轻松地)告诉你这是做了什么。例如,instanceof始终返回ImmutableList.copyOf()。作为一个实现细节,它使用ImmutableList来避免构造新的instanceof,但这不是可接受地提供预期行为的必要条件。

顺便说一下,当我在挖掘Guava的源代码时,看到你的名字路易斯很有趣。我发誓我不知道!

答案 2 :(得分:4)

您的第一个案例是我不使用instanceof运算符的示例,但请查看类是否相等:

o != null && o.getClass() == this.getClass()

这样可以避免将A extends BB的实例视为相等

我可以立即想到其他情况,但我非常确定有更多有效案例可供使用

  • 工厂实例,例如,您有一个canCreatecreate方法,它接收一般接口作为参数。每个工厂都可以处理接口的特定实现,因此需要instanceof。仅定义工厂抽象类/接口中的接口允许例如编写复合工厂
  • 复合实现(如我的第一个示例所示)

答案 3 :(得分:-1)

正如您所提到的,instanceof的“正确”用法相当有限。据我所知,你基本上总结了两个主要用途。

但是,您可以稍微概括一下您的陈述,但如下所示:

  1. 在必要的演员表之前进行类型检查。
  2. 实现依赖于非常特定的类实例的特殊情况