java:结合instanceof和cast?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3949198/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 04:05:31  来源:igfitidea点击:

java: combined instanceof and cast?

javacastinginstanceofdynamic-cast

提问by Albert

(Please no advise that I should abstract Xmore and add another method to it.)

(请不要建议我应该抽象X更多并添加另一种方法。)

In C++, when I have a variable xof type X*and I want to do something specific if it is also of type Y*(Ybeing a subclass of X), I am writing this:

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

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

The same thing seems not possible in Java (or is it?).

同样的事情在 Java 中似乎是不可能的(或者是吗?)。

I have read this Java code instead:

我已经阅读了这个 Java 代码:

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

Sometimes, when you don't have a variable xbut it is a more complex expression instead, just because of this issue, you need a dummy variable in Java:

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

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

(Common thing is that something()is iterator.next(). And there you see that you also cannot really call that just twice. You really need the dummy variable.)

(常见的是,something()iterator.next()。还有你看,你也不能真正称之为两次即可。你真的需要虚拟变量。)

You don't really need xat all here -- you just have it because you cannot do the instanceofcheck at once with the cast. Compare that again to the quite common C++ code:

x在这里根本不需要——你只是拥有它,因为你不能instanceof立即对演员进行检查。再次将其与非常常见的 C++ 代码进行比较:

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

Because of this, I have introduced a castOrNullfunction which makes it possible to avoid the dummy variable x. I can write this now:

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

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

Implementation of castOrNull:

实施castOrNull

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

Now, I was told that using this castOrNullfunction in that way is an evil thing do to. Why is that? (Or to put the question more general: Would you agree and also think this is evil? If yes, why so? Or do you think this is a valid (maybe rare) use case?)

现在,有人告诉我,以这种castOrNull方式使用此功能对. 这是为什么?(或者更笼统地提出问题:您是否同意并认为这是邪恶的?如果是,为什么会这样?或者您认为这是一个有效的(可能很少见)用例?)



As said, I don't want a discussion whether the usage of such downcast is a good idea at all. But let me clarify shortly why I sometimes use it:

如前所述,我根本不想讨论使用这种向下转型是否是一个好主意。但让我简短地澄清一下为什么我有时会使用它:

  1. Sometimes I get into the case where I have to choose between adding another new method for a very specific thing (which will only apply for one single subclass in one specific case) or using such instanceofcheck. Basically, I have the choice between adding a function doSomethingVeryVerySpecificIfIAmY()or doing the instanceofcheck. And in such cases, I feel that the latter is more clean.

  2. Sometimes I have a collection of some interface / base class and for all entries of type Y, I want to do something and then remove them from the collection. (E.g. I had the case where I had a tree structure and I wanted to delete all childs which are empty leafs.)

  1. 有时我会遇到这样的情况,我必须在为非常具体的事情添加另一种新方法(在一个特定情况下仅适用于一个子类)或使用此类instanceof检查之间做出选择。基本上,我可以选择添加功能doSomethingVeryVerySpecificIfIAmY()或进行instanceof检查。而在这种情况下,我觉得后者更干净。

  2. 有时我有一些接口/基类的集合,对于 type 的所有条目Y,我想做一些事情然后从集合中删除它们。(例如,我有一个树结构,我想删除所有空叶子的孩子。)

回答by Stephen C

Now, I was told that using this castOrNull function in that way is an evil thing do to. Why is that?

现在,我被告知以这种方式使用这个 castOrNull 函数是一件邪恶的事情。这是为什么?

I can think of a couple of reasons:

我能想到几个原因:

  • It is an obscure and tricky way of doing something very simple. Obscure and tricky code is hard to read, hard to maintain, a potential source of errors (when someone doesn't understand it) and thereforeevil.

  • The obscure and tricky way that the castOrNullmethod works most likely cannot be optimized by the JIT compiler. You'll end up with at least 3 extra method calls, plus lots of extra code to do the type check and cast reflectively. Unnecessary use of reflection is evil.

  • 这是做一些非常简单的事情的一种晦涩难懂的方法。晦涩难懂的代码难以阅读,难以维护,是潜在的错误来源(当有人不理解时),因此是邪恶的。

  • castOrNullJIT 编译器很可能无法优化该方法工作的晦涩难懂的方式。您最终会得到至少 3 个额外的方法调用,以及大量额外的代码来进行类型检查和反射性转换。不必要地使用反射是邪恶的。

(By contrast, the simple way (with instanceoffollowed by a class cast) uses specific bytecodes for instanceof and class casting. The bytecode sequences can almost certainly will be optimized so that there is no more than one null check and no more that one test of the object's type in the native code. This is a common pattern that should be easy for the JIT compiler to detect and optimize.)

(相比之下,简单的方法(instanceof后跟一个类转换)使用特定的字节码来进行 instanceof 和类转换。几乎可以肯定字节码序列会被优化,以便不超过一次空检查和一次测试本机代码中对象的类型。这是一种常见模式,应该很容易让 JIT 编译器检测和优化。)

Of course, "evil" is just another way of saying that you REALLY shouldn't do this.

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

Neither of your two added examples, make use of a castOrNullmethod either necessary or desirable. IMO, the "simple way" is better from both the readability and performance perspectives.

您添加的两个示例都没有使用castOrNull必要或可取的方法。IMO,从可读性和性能的角度来看,“简单方式”更好。

回答by TofuBeer

In most well written/designed Java code the use of instanceof and casts never happens. With the addition of generics many cases of casts (and thus instanceof) are not needed. They do, on occasion still occur.

在大多数编写/设计良好的 Java 代码中,instanceof 和强制转换的使用从未发生过。随着泛型的添加,很多情况下的类型转换(以及 instanceof)就不再需要了。他们这样做,有时仍然会发生。

The castOrNull method is evil in that you are making Java code look "unnatural". The biggest problem when changing from one language to another is adopting the conventions of the new language. Temporary variables are just fine in Java. In fact all your method is doing is really hiding the temporary variable.

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

If you are finding that you are writing a lot of casts you should examine your code and see why and look for ways to remove them. For example, in the case that you mention adding a "getNumberOfChildren" method would allow you to check if a node is empty and thus able to prune it without casting (that is a guess, it might not work for you in this case).

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

Generally speaking casts are "evil" in Java because they are usually not needed. Your method is more "evil" because it is not written in the way most people would expect Java to be written.

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

That being said, if you want to do it, go for it. It isn't actually "evil" just not "right" way to do it in Java.

话虽如此,如果你想做,就去做吧。在 Java 中,它实际上并不是“邪恶”的,只是不是“正确”的方式。

回答by Péter T?r?k

IMHO your castOrNullis not evil, just pointless. You seem to be obsessed with getting rid of a temporary variable and one line of code, while to me the bigger question is why you need so many downcasts in your code? In OO this is almost always a symptom of suboptimal design. And I would prefer solving the root cause instead of treating the symptom.

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

回答by Sergei Kuzmin

Starting Java 14 you should be able to do instanceofand cast at the same time. See https://openjdk.java.net/jeps/305.

从 Java 14 开始,您应该能够同时执行instanceof和转换。请参阅https://openjdk.java.net/jeps/305

Code example:

代码示例:

if (obj instanceof String s) {
    // can use s here
} else {
    // can't use s here
}

The variable s in the above example is defined if instanceofevaluates to true. The scope of the variable depends on the context. See the link above for more examples.

上面例子中的变量 s 是在instanceof计算结果为真时定义的。变量的范围取决于上下文。有关更多示例,请参阅上面的链接。

回答by Thomas

I don't know exactly why the person said that it was evil. However one possibility for their reasoning was the fact that you were catching an exception afterwards rather than checking before you casted. This is a way to do that.

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

public static <T> T castOrNull(Object obj, Class<T> clazz) {
    if ( clazz.isAssignableFrom(obj.getClass()) ) {
        return clazz.cast(obj);
    } else {
        return null;
    }
}

回答by juckele

Java Exceptions are slow. If you're trying to optimize your performance by avoiding a double cast, you're shooting yourself in the foot by using exceptions in lieu of logic. Never rely on catching an exception for something you could reasonably check for and correct for (exactly what you're doing).

Java 异常很慢。如果您试图通过避免双重转换来优化您的性能,那么您就是在使用异常代替逻辑来打自己的脚。永远不要依赖捕捉异常你可以合理检查和纠正(正是你在做什么)。

How slow are Java exceptions?

Java 异常有多慢?