为什么在一个instanceOf之后施放?

在下面的例子中(来自我的coursepack),我们想要给Square实例c1提供一些其他对象p1的引用,但前提是这两个对象是兼容的类型。

 if (p1 instanceof Square) {c1 = (Square) p1;} 

我在这里不明白的是,我们首先检查p1确实是一个Square ,然后我们仍然投射它。 如果它是一个Square ,为什么演员呢?

我怀疑答案在于明显和实际类型之间的区别,但我很困惑……

编辑:
编译器将如何处理:

 if (p1 instanceof Square) {c1 = p1;} 

EDIT2:
问题是instanceof检查实际类型而不是表观类型吗? 然后演员改变了表观类型?

谢谢,

JDelage

请记住,您始终可以将Square的实例分配给inheritance链上方的类型。 然后,您可能希望将较不具体的类型强制转换为更具体的类型,在这种情况下,您需要确保您的强制转换有效:

 Object p1 = new Square(); Square c1; if(p1 instanceof Square) c1 = (Square) p1; 

编译器不会推断出,因为您在块中,所以您已成功检查对象的类型。 仍然需要显式强制转换来告诉编译器您希望将对象引用为不同的类型。

 if (p1 instanceof Square) { // if we are in here, we (programmer) know it's an instance of Square // Here, we explicitly tell the compiler that p1 is a Square c1 = (Square) p1; } 

在C#中,你可以在1次调用中进行检查和强制转换:

 c1 = p1 as Square; 

这会将p1转换为Square,如果转换失败,则c1将设置为null

测量某个物体是否适合盒子,并将其实际放入盒子之间有区别。 instanceof是前者,而铸造是后者。

因为这种特殊的语法糖还没有添加到语言中。 我认为它是针对Java 7提出的,但它似乎没有输入项目硬币

旧代码无法正常工作

毕竟,impield castfunction是合理的,但由于向后兼容性,我们很难将此FR实现为java。

看到这个:

 public class A { public static void draw(Square s){...} // with impield cast public static void draw(Object o){...} // without impield cast public static void main(String[] args) { final Object foo = new Square(); if (foo instanceof Square) { draw(foo); } } } 

当前的JDK将编译第二个声明的方法的用法。 如果我们在java中实现这个FR,它将编译为使用第一个方法!

例如,如果从Object类型移交p1,编译器就不会知道它实际上是Square的实例,因此无法访问Methods等。 if只检查某种类型以返回true / false,但这不会改变变量p1的类型。

变量p1具有它开始的任何类型 – 比方说Shape。 p1是一个Shape,只有一个Shape,无论它的当前内容恰好是Square。 你可以在Square上调用 – 让我们说 – side(),但不能在Shape上调用。 只要您通过变量p1识别有问题的实体,其类型为Shape,就不能在其上调用side(),因为变量的类型。 Java的类型系统的工作方式,如果你碰巧知道它是一个Square,你可以调用p1.side(),你总是可以调用p1.side()。 但是p1不仅可以容纳方形形状,还可以容纳(例如)圆形形状,当p1持有圆形时调用p1.side()将会出错。 所以你需要另一个变量来表示你碰巧知道的形状是一个Square,一个类型为Square的变量。 这就是为什么演员阵容是必要的。

如果c1被声明为Square类型,则需要进行转换。 如果它被声明为Object则不需要转换。

完成测试是为了防止运行时出现ClassCastExceptions

 Square c1 = null; if (p1 instanceof Square) { c1 = (Square) p1; } else { // we have a p1 that is not a subclass of Square } 

如果你绝对肯定p1是一个Square ,那么你就不必测试了。 但是把它留给私人方法……

不要讨厌,但你必须告诉编译器你想做什么,因为它可以让你猜测你想要做什么。 当然,您可能会想,“如果我正在检查对象的类型,那么显然必须意味着我想将它转换为该类型。” 但谁说呢? 也许这就是你要做的事情,也许事实并非如此。

当然,在一个简单的情况下

 if (x instanceof Integer) { Integer ix=(Integer) x; ... 

我的意图非常明显。 或者是吗? 也许我真正想要的是:

 if (x instanceof Integer || x instanceof Double) { Number n=(Number) x; ... work with n ... 

或者,如果我写道:

 if (x instanceof Integer || x instanceof String) 

您希望编译器接下来做什么? x应该采用什么类型?

请注意,instanceof已经过时或者是一个坏主意:它肯定会被误用。 我最近参与了一个程序,在这个程序中,原作者创建了六个类,这些类都是页面和页面长,但彼此相同,唯一明显的原因是他可以说“x instanceof classA”与“ x instanceof classB“等等。也就是说,他使用该类作为类型标志。 最好只有一个类并为各种类型添加枚举。 但也有很多非常好的用途。 也许最明显的是:

 public MyClass { int foo; String bar; public boolean equals(Object othat) { if (!(othat instanceof MyClass)) return false; MyClass that=(MyClass) othat; return this.foo==that.foo && this.bar.equals(that.bar); } ... etc ... } 

如果不使用instanceof,你会怎么做? 您可以使参数为MyClass类型而不是Object。 但是后来甚至没有办法用通用对象来调用它,这在很多情况下都是非常需要的。 实际上,也许我想要一个集合,比如包括字符串和整数,我希望比较不同的类型只是返回false。