为什么要在instanceOf之后进行强制转换?
在下面的示例中(来自我的课程包),我们想要为 Square
实例 c1
提供其他对象 p1
的引用,但仅限如果这两个是兼容类型。
if (p1 instanceof Square) {c1 = (Square) p1;}
我在这里不明白的是,我们首先检查 p1
确实是一个 Square
,然后我们仍然将其转换。如果它是一个 Square
,为什么要进行强制转换?
我怀疑答案在于表面类型和实际类型之间的区别,但我仍然很困惑......
编辑:
编译器将如何处理:
if (p1 instanceof Square) {c1 = p1;}
Edit2:
问题是 instanceof
检查的是 actual 类型而不是apparent 类型吗?然后强制转换会改变明显类型?
In the example below (from my coursepack), we want to give to the Square
instance c1
the reference of some other object p1
, but only if those 2 are of compatible types.
if (p1 instanceof Square) {c1 = (Square) p1;}
What I don't understand here is that we first check that p1
is indeed a Square
, and then we still cast it. If it's a Square
, why cast?
I suspect the answer lies in the distinction between apparent and actual types, but I'm confused nonetheless...
Edit:
How would the compiler deal with:
if (p1 instanceof Square) {c1 = p1;}
Edit2:
Is the issue that instanceof
checks for the actual type rather than the apparent type? And then that the cast changes the apparent type?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
正如 Leroy 提到的,Java 14 为 instanceof 引入了模式匹配。因此,您可以将 instanceof 检查和类型转换完全组合在一个表达式中:
可以重写为此
功能已在 Java 16 (JEP 394) 中完成。对于以下版本,请参阅此链接从 IDE 中启用此预览功能,例如IntelliJ、Eclipse 和 STS。
As Leroy mentioned, Java 14 introduces pattern matching for instanceof. So, you can combine both instanceof check and typecast altogether in a single expression:
can be rewritten as
This feature is finalized in Java 16 (JEP 394). For the below versions, refer this link to enable this preview feature from IDEs such as IntelliJ, Eclipse, and STS.
例如,如果您将 p1 作为 Object 类型移交,编译器将不知道它实际上是 Square 的实例,因此方法等将无法访问。 if 只是检查某种类型是否返回 true/false,但这不会更改变量 p1 的类型。
E.g. If you hand over p1 as of type Object, the compiler wouldn't know that it is in fact an instance of Square, so that Methods etc. wouldn't be accessible. The if simply checks for a certain type to return true/false, but that doesn't change the type of the variable p1.
进行测试是为了防止运行时出现
ClassCastExceptions
:如果您绝对肯定
p1
是Square
,那么您就没有进行测试。但将其留给私有方法......The test is done to prevent from
ClassCastExceptions
at runtime:If you're absolutly positive that
p1
is aSquare
, then you don't have to test. But leave this to private methods...变量 p1 具有其开头的任何类型 - 比如说 Shape。 p1 是一个 Shape,而且只是一个 Shape,无论它当前的内容恰好是一个 Square。您可以在 Square 上调用 - 比方说 - side(),但不能在 Shape 上调用。只要您通过类型为 Shape 的变量 p1 标识相关实体,就无法对其调用 side(),因为变量的类型。 Java 类型系统的工作方式是,如果您在知道它是 Square 时可以调用 p1.side(),那么您总是可以调用 p1.side()。但是 p1 不仅可以容纳正方形,还可以容纳圆形,当 p1 容纳圆形时调用 p1.side() 将是错误的。所以你需要另一个变量来表示Shape,你碰巧知道它是一个Square,一个类型为Square的变量。这就是为什么演员阵容是必要的。
The variable p1 has whatever type it started with - let's say Shape. p1 is a Shape, and only a Shape, no matter that its current contents happen to be a Square. You can call - let's say - side() on a Square, but not on a Shape. So long as you are identifying the entity in question via the variable p1, whose type is Shape, you can't call side() on it, because of the type of the variable. The way Java's type system works, if you can call p1.side() when you happen to know it's a Square, you can always call p1.side(). But p1 can hold not just Square Shapes, but also (say) Circle Shapes, and it would be an error to call p1.side() when p1 held a Circle. So you need another variable to represent the Shape which you happen to know is a Square, a variable whose type is Square. That's why the cast is necessary.
不要令人讨厌,但您必须告诉编译器您想要做什么,因为另一种选择是让编译器猜测您想要做什么。当然,您可能会想,“如果我正在检查对象的类型,显然这一定意味着我想将其转换为该类型。”但谁说呢?也许这就是你想做的,也许不是。
当然,在像我这样的简单情况下,
我的意图是非常明显的。或者是吗?也许我真正想要的是:
或者如果我写:
你希望编译器下一步做什么? x 应该采用什么类型?
RE 评论说instanceof 已经过时或者是一个坏主意:它肯定会被误用。我最近在开发一个程序,其中原作者创建了六个类,结果都是一页又一页长,但彼此相同,并且拥有它们的唯一明显原因是他可以说“x instanceof classA”与“ x instanceof classB”等。也就是说,他使用了类作为类型标志。最好只拥有一个类并为各种类型添加一个枚举。但也有很多非常好的用途。也许最明显的是:
如果不使用instanceof,你会如何做到这一点?您可以使参数的类型为 MyClass 而不是 Object。但是,甚至无法使用通用对象来调用它,这在许多情况下可能是非常理想的。事实上,也许我希望一个集合包含字符串和整数,并且我希望不同类型的比较简单地返回 false。
Not to be obnoxious, but you have to tell the compiler what you want to do because the alternative would be for it to guess what you're trying to do. Sure, you might think, "If I'm checking the type of an object, OBVIOUSLY that must mean that I want to cast it to that type." But who says? Maybe that's what you're up to and maybe it isn't.
Sure, in a simple case like
My intent is pretty obvious. Or is it? Maybe what I really want is:
Or what if I wrote:
What would you expect the compiler to do next? What type should it assume for x?
RE the comments that instanceof is obsolete or otherwise a bad idea: It can certainly be mis-used. I recently worked on a program where the original author created six classes that all turned out to be pages and pages long, but identical to each other, and the only apparent reason for having them was so he could say "x instanceof classA" versus "x instanceof classB", etc. That is, he used the class as a type flag. It would have been better to just have one class and add an enum for the various types. But there are also plenty of very good uses. Perhaps the most obvious is something like:
How would you do that without using instanceof? You could make the parameter be of type MyClass instead of Object. But then there's be no way to even call it with a generic Object, which could be highly desirable in many cases. Indeed, maybe I want a collection to include, say, both Strings and Integers, and I want comparisons of unlike types to simply return false.
如果
c1
被声明为Square
类型,则需要进行强制转换。如果它被声明为Object
,则不需要强制转换。If
c1
is declared as a type ofSquare
then casting is required. If it is a declared as anObject
then casting is not needed.旧代码将无法正常工作
隐含的转换功能毕竟是合理的,但由于向后兼容性,我们在将这个 FR 实现到 java 时遇到了麻烦。
看这个:
当前的 JDK 会编译第二个声明方法的用法。
如果我们用java实现这个FR,它将编译为使用第一种方法!
Old code will not work correctly
The implied cast feature is justified after all but we have trouble to implement this FR to java because of backward-compatibility.
See this:
The current JDK would compile the usage of the second declared method.
If we implement this FR in java, it would compile to use the first method!
???? JDK 14
We finally implemented this feature in JDK 14. As you might have noticed you can declare a new variable within the instanceof-linkage. This new variable has been defined by the value of a automatically downcast to the specified type.
请记住,您始终可以将 Square 的实例分配给继承链上更高的类型。然后,您可能希望将不太具体的类型转换为更具体的类型,在这种情况下,您需要确保您的转换是有效的:
Keep in mind, you could always assign an instance of Square to a type higher up the inheritance chain. You may then want to cast the less specific type to the more specific type, in which case you need to be sure that your cast is valid:
编译器不会推断,由于您位于该块中,因此您已成功检查对象的类型。仍然需要显式强制转换来告诉编译器您希望将该对象引用为不同类型。
在 C# 中,您可以在 1 次调用中进行检查和转换:
这会将
p1
转换为 Square,如果转换失败,c1 将设置为null
。The compiler does not infer that since you are in the block, you have done a successful check for the type of the object. An explicit cast is still required to tell the compiler that you wish to reference the object as a different type.
In C# you can do the check and the cast in 1 call:
This will cast
p1
to a Square, and if the cast fails, c1 will be set tonull
.为了提供对此的更新,Java 14 现在提供了 instanceof 的模式匹配,这允许您一次性检查和转换。
这(旧方法):
可以简化为:
Just to provide an update on this, Java 14 now provides pattern matching for instanceof, this allows you to check and cast in one fell swoop.
This (old way):
Can be simplified to this:
测量某个物体是否适合放入盒子中与实际将其放入盒子中是有区别的。
instanceof
是前者,casting 是后者。There's a difference between measuring if some object will fit in a box, and actually putting it in the box.
instanceof
is the former, and casting is the latter.因为这个特定的语法糖尚未添加到语言中。我认为它是针对Java 7提出的,但似乎没有进入项目币
Because this particular syntactic sugar is not yet added to the language. I think it was proposed for Java 7, but it doesn't seem to have entered project coin