java:instanceof和cast的组合?

问题描述 投票:6回答:6

((请不要建议我应该进一步抽象X并向其中添加其他方法。)

在C ++中,当我有一个类型为x的变量X*时,如果它也是Y*类型(YX的子类),我想做一些特定的事情,这个:

if(Y* y = dynamic_cast<Y*>(x)) {
    // now do sth with y
}

在Java中似乎不可能(或者是吗?)。

我已阅读此Java代码:

if(x instanceof Y) {
    Y y = (Y) x;
    // ...
}

有时,当您没有变量x但它是一个更复杂的表达式时,正因为此问题,您需要在Java中使用一个虚拟变量:

X x = something();
if(x instanceof Y) {
    Y y = (Y) x;
    // ...
}
// x not needed here anymore

((通常,something()iterator.next()。在那里您看到您也不能真正只两次调用它。您确实需要虚拟变量。)

您在这里根本不需要x -您只需要它,因为您无法立即对演员表进行instanceof检查。再次将其与非常常见的C ++代码进行比较:

if(Y* y = dynamic_cast<Y*>( something() )) {
    // ...
}

因此,我引入了castOrNull函数,可以避免使用虚拟变量x。我现在可以写这个:

Y y = castOrNull( something(), Y.class );
if(y != null) {
    // ...
}

castOrNull的实现:

public static <T> T castOrNull(Object obj, Class<T> clazz) {
    try {
        return clazz.cast(obj);
    } catch (ClassCastException exc) {
        return null;
    }
}

现在,有人告诉我using this castOrNull function in that way is an evil thing do to。这是为什么? (或者更笼统地提出这个问题:您是否会同意并且也认为这是邪恶的?如果是,为什么会这样?或者您认为这是一个有效的(也许是罕见的)用例?)


如前所述,我不想讨论使用这种垂头丧气是否是个好主意。但是,请允许我简短说明为什么有时使用它:

  1. 有时候我不得不选择为一种非常特定的事物添加另一种新方法(在一种特定情况下仅适用于单个子类)或使用这种castOrNull检查之间进行选择。基本上,我可以选择添加功能instanceof还是进行doSomethingVeryVerySpecificIfIAmY()检查。在这种情况下,我觉得后者更干净。

  2. 有时候我有一些接口/基类的集合,对于instanceof类型的所有条目,我想做些什么,然后将它们从集合中删除。 (例如,我有一个树状结构,我想删除所有为空叶的孩子。)

java casting instanceof dynamic-cast
6个回答
8
投票

现在,有人告诉我,以这种方式使用此castOrNull函数是很邪恶的。为什么会这样?

我可以想到几个原因:

  • 这是做起来非常简单的晦涩而棘手的方法。晦涩难懂的代码难以阅读,难以维护,潜在的错误源(当有人不理解时)和there因此邪恶。

  • J0编译器无法优化Y方法工作的晦涩和棘手的方式。最后,您将至少获得3个额外的方法调用,以及大量额外的代码来进行类型检查和反射转换。不必要地使用反射是邪恶的。

(相反,最简单的方法(castOrNull后跟一个类强制转换)对instanceof和类强制转换使用特定的字节码。几乎可以肯定,字节码序列将被优化,以使不超过一个空检查且没有不仅仅是在本机代码中对对象类型的测试。这是一种常见的模式,对于JIT编译器来说,应该易于检测和优化。)

当然,“邪恶”只是您不应该这样做的另一种说法。

您添加的两个示例均不使用instanceof方法,这是必需的还是期望的。 IMO,从可读性和性能的角度来看,“简单方法”更好。


5
投票

在大多数编写良好/设计良好的Java代码中,永远不会使用instanceof和cast。通过添加泛型,不需要许多强制类型转换(因此,instanceof)。它们确实存在,有时仍会发生。

castOrNull方法是邪恶的,因为您使Java代码看起来“不自然”。从一种语言更改为另一种语言时,最大的问题是采用新语言的约定。临时变量在Java中很好用。实际上,您的所有方法实际上是在隐藏临时变量。

如果发现正在编写大量强制类型转换,则应检查代码,看看原因,并寻找删除它们的方法。例如,在提到添加“ getNumberOfChildren”方法的情况下,将允许您检查节点是否为空,从而能够在不进行强制转换的情况下对其进行修剪(这是猜测,在这种情况下,它可能对您不起作用)。

一般来说,强制转换在Java中是“邪恶的”,因为通常不需要它们。您的方法更“邪恶”,因为它的编写方式与大多数人期望的Java编写方式不同。

话虽这么说,如果你想做,那就去吧。在Java中,这实际上不是“邪恶”,也不是“正确”的方式。


4
投票

恕我直言,您的castOrNull不是邪恶的,只是毫无意义的。您似乎迷上了摆脱一个临时变量和一行代码的问题,而对我来说,更大的问题是为什么您的代码中需要这么多下调??在OO中,这几乎始终是次优设计的症状。而且,我宁愿解决根本原因而不是治疗症状。


2
投票

我不知道为什么那个人说这是邪恶的。但是,他们推理的一种可能性是,您随后在捕获异常而不是在投射之前进行检查。这是一种方法。

castOrNull

1
投票

启动Java 14,您应该能够同时执行public static <T> T castOrNull(Object obj, Class<T> clazz) { if ( clazz.isAssignableFrom(obj.getClass()) ) { return clazz.cast(obj); } else { return null; } } 和强制转换。参见instanceof

代码示例:

https://openjdk.java.net/jeps/305

以上示例中的变量s是在if (obj instanceof String s) { // can use s here } else { // can't use s here } 计算为true时定义的。变量的范围取决于上下文。有关更多示例,请参见上面的链接。


0
投票

Java异常很慢。如果您尝试通过避免两次重投来优化性能,那么您将使用异常代替逻辑来使自己陷入困境。绝对不要依赖于可以合理检查和纠正(确实在做的事情)的异常。

instanceof

© www.soinside.com 2019 - 2024. All rights reserved.