铸造的优点和缺点与提供返回所需类型的方法(Java)

时间:2011-04-09 00:46:43

标签: java casting

我正在努力学习一个我正在贡献的框架,并提出了一个有趣的问题。 编辑: 我正在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()来说不是问题,因为它只是每种类型的一种方法,并且可以根据偏好为每种类型添加或不添加。

5 个答案:

答案 0 :(得分:4)

如果目标是测试对象的类型并将其强制转换,那么我认为创建/使用自定义isXyzasXyz方法没有任何价值。您最终会得到一些额外的方法,这些方法对代码的可读性几乎没有影响。

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并抛出自定义异常。

  • 不太可能弄清楚在第二种情况下确实只需要一种类型测试。 (它可能不是第一种情况......但更有可能。)

但不管怎样,性能差异都会很小。


总结,花哨的方法并不值得付出努力,尽管它们没有任何真正的伤害。


现在,如果isXyzasXyz方法正在测试对象的状态(不仅仅是对象的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并构建适当的类。