正如我一直都明白的那样,主要案例在哪里 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 对象进入 Strings with instanceof就像在做的那样 Joiner.toString(Object)。
做复杂的异常处理
显然,“正确”的事情是使用a try/catch 阻止(虽然真的,那是在做 instanceof 检查已经),但有时你有更复杂的处理逻辑,值得使用条件块或将处理传递给单独的方法,例如拉出原因或具有特定于实现的处理。可以在中找到一个例子 SimpleTimeLimiter.throwCause()。
看待这种行为突出的一件事就是几乎所有这些都在解决问题 我不应该解决。它们在库代码中很有用,例如在 Iterables,但如果我正在实现这种行为,我应该问自己,是否有没有库或实用程序可以解决这个问题。
在所有情况下,我都会这么说 instanceof 检查应该只在内部用作实现细节 - 也就是说依赖于任何方法的调用者 instanceof 检查不应该(轻易)告诉你你做了什么。例如, ImmutableList.copyOf() 总是返回一个 ImmutableList。作为它使用的实现细节 instanceof 避免构建新的 ImmutableLists,但没有必要接受提供预期的行为。
顺便说一句,当我在挖掘Guava的源代码时,看到你的名字路易斯很有趣。我发誓我不知道!
你的第一个案例是我不会使用的例子 instanceof 运算符,但看看类是否相等:
o != null && o.getClass() == this.getClass()
这将避免一个实例 A extends B 和 B 被认为是平等的
我可以立即想到的其他情况,但我很确定有更多有效的案例
- 您拥有的工厂实例,例如a
canCreate 和 create 接收通用接口作为参数的方法。每个工厂都可以处理接口的特定实现,因此需要一个 instanceof。仅定义工厂抽象类/接口中的接口允许例如编写复合工厂
- 复合实现(如我的第一个例子中所示)
正如你所提到的,“正确”的用途 instanceof 相当有限。据我所知,你基本上总结了两个主要用途。
但是,您可以稍微概括一下您的语句,如下所示:
- 在必要的演员表之前进行类型检查。
- 实现依赖于非常特定的类实例的特殊情况