我正在努力学习一个我正在贡献的框架,并提出了一个有趣的问题。 编辑: 我正在Okapi框架中执行一些基本过滤器,如this guide中所述,请注意,过滤器必须返回不同的事件类型才有用,并且必须通过引用使用(因为稍后可能在其他过滤器中使用相同的资源)。这是我正在使用的代码:
while (filter.hasNext()) {
Event event = filter.next();
if (event.isTextUnit()) {
TextUnit tu = (TextUnit)event.getResource();
if (tu.isTranslatable()) {
//do something with it
}
}
}
请注意第4行的TextUnit对象的资源转换。这有效,我知道它是一个TextUnit,因为isTextUnit()
的事件将始终具有TextUnit资源。但是,另一种方法是将一个asTextUnit()
方法添加到IResource接口,该接口将事件作为TextUnit返回(以及每个公共资源类型的等效方法),以便该行变为:
TextUnit tu = event.getResource().asTextUnit;
另一种方法可能是在TextUnit本身提供静态转换方法,方法如下:
TextUnit tu = TextUnit.fromResource(event.getResource());
我的问题是:以某种方式做某事的理由是什么?是否存在绩效差异?
我可以考虑使用asTextUnit()
(或.fromResource
)的主要优点是,如果有人试图将资源作为错误的类型(例如,使用类似{的消息),则可能抛出更合适的异常{1}}或"Cannot get this RawDocument type resource as a TextUnit - use asRawDocument()"
)。
我可以用"The resource is not a TextUnit"
来思考的主要缺点是每种资源类型都必须实现所有方法(其中大多数只会引发异常),如果添加了另一种主要资源类型,那么进行一些重构以将新方法添加到每种资源类型(尽管没有理由必须为每种可能的类型定义.asTextUnit()
方法,但是可以使用较少的公共资源,尽管这会导致不一致方法)。这对于.asSomething()
来说不是问题,因为它只是每种类型的一种方法,并且可以根据偏好为每种类型添加或不添加。
答案 0 :(得分:4)
如果目标是测试对象的类型并将其强制转换,那么我认为创建/使用自定义isXyz
和asXyz
方法没有任何价值。您最终会得到一些额外的方法,这些方法对代码的可读性几乎没有影响。
Re:关于适当的异常消息的观点,我想说它很可能不值得。可以合理地假设当TextUnit
被预期时没有TextUnit
是某个地方的某个错误的症状。 IMO,尝试为bug提供“用户友好”诊断是不值得的。信息所针对的人是Java程序员,对于那个人来说,常规ClassCastException(和源代码)的默认消息和堆栈跟踪提供了所需的所有信息。 (将其翻译成漂亮的语言不会增加真正的价值。)
另一方面,两种形式之间的性能差异可能不大。但请考虑一下:
if (x instanceof Y) {
((Y) x).someYMethod();
}
与
if (x.isY()) {
x.asY().someYMethod();
}
boolean isY(X x) { return x instanceof Y; }
Y asY(X x) { return (Y) x; }
与第二个相比,优化器可能能够做得更好。
在第二种情况下,它可能不会内联方法调用,特别是如果它被更改为使用instanceof
并抛出自定义异常。
不太可能弄清楚在第二种情况下确实只需要一种类型测试。 (它可能不是第一种情况......但更有可能。)
但不管怎样,性能差异都会很小。
总结,花哨的方法并不值得付出努力,尽管它们没有任何真正的伤害。
现在,如果isXyz
或asXyz
方法正在测试对象的状态(不仅仅是对象的Java类型),或者asXyz
正在返回包装器,那么答案会有所不同......
答案 1 :(得分:2)
你也可以去
if (event instanceof TextUnit) {
// ...
}
并省去麻烦。
要回答有关是否转到asTextUnit()
与TextUnit.fromResource
的问题,性能差异将取决于您实际实施这些方法的方式。
对于静态转换器,您可以创建并返回类型为TextUnit
的新对象。但是,在成员函数的情况下,您可以简单地返回this
或者您可以创建一个返回一个新对象 - 取决于您的用例。
无论哪种方式,似乎instanceof
可能是最干净的方式。
答案 2 :(得分:2)
如果您的过滤器已扩展 - 或包装 - 仅返回文本单元事件,该怎么办?事实上,如果只返回文本单元事件的资源呢?然后你的循环会简单得多。我认为干净的方法是第二个过滤器,它只返回文本单元事件,然后,比方说,一个Extractor,它返回正确的强制转换资源。
答案 3 :(得分:1)
如果你有一个公共基类,那么每个派生类都可以有一个asXMethod,并且不需要重构所有派生类:
abstract class Base {
A asA () { throw new InstantiationException ("not an A"); }
B asB () { throw new InstantiationException ("not an B"); }
C asC () { throw new InstantiationException ("not an C"); }
// much more ...
}
class A extends Base {
A asA () { /* hard work */ return new A (); }
// no asB, asC requiered
}
class B extends Base {
B asB () { /* hard work */ return new B (); }
// no asA, asC required
}
// and so on.
看起来很聪明。对于新的N类,只需向Base添加一个新方法,所有派生类都可以获得它。只需要N实现asN。
但它闻起来。
如果B总是会失败,为什么B应该有一个方法?那不是一个好的设计。如果没有触发,则生成器中的异常便宜。只抛出异常可能代价高昂。
答案 4 :(得分:0)
是的,有差异。创建新的不可变元素比投射更好。将所有可序列化数据(非瞬态或可计算数据)传递给Builder并构建适当的类。