[英]Why cast after an instanceOf?
在下面的示例中(来自我的课程包),我们希望为Square
实例c1
其他对象p1
的引用,但前提是这两个对象是兼容类型。
if (p1 instanceof Square) {c1 = (Square) p1;}
我在这里不明白的是,我们首先检查p1
确实是一个Square
,然后我们仍然投射它。 如果是Square
,为什么要投?
我怀疑答案在于明显类型和实际类型之间的区别,但我仍然感到困惑......
编辑:
编译器将如何处理:
if (p1 instanceof Square) {c1 = p1;}
编辑2:
instanceof
检查实际类型而不是表观类型的问题是什么? 然后演员改变了明显的类型?
请记住,您始终可以将 Square 的实例分配给继承链上层的类型。 然后,您可能希望将不太具体的类型转换为更具体的类型,在这种情况下,您需要确保您的转换有效:
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
。
隐含的强制转换功能毕竟是合理的,但由于向后兼容,我们很难将这个 FR 实现到 java。
看到这个:
public class A {
public static void draw(Square s){...} // with implied cast
public static void draw(Object o){...} // without implied cast
public static void main(String[] args) {
final Object foo = new Square();
if (foo instanceof Square) {
draw(foo);
}
}
}
当前 JDK 将编译第二个声明方法的用法。 如果我们在java中实现这个FR,它会编译为使用第一种方法!
我们最终在 JDK 14 中实现了此功能。您可能已经注意到,您可以在 instanceof-linkage 中声明一个新变量。 这个新变量已由自动向上转换为指定类型的值定义。
if (any instanceof String s) {
System.out.println(s);
}
测量某个物体是否适合放在盒子中与实际将其放入盒子中是有区别的。 instanceof
是前者,而casting 是后者。
只是为了提供更新,Java 14 现在提供了 instanceof 的模式匹配,这使您可以一举检查和转换。
这(旧方式):
void outputValue(Object obj) {
if (obj instanceof String) { // Compare
String aString = (String) obj; // New variable & explicit casting
System.out.println(aString.toUpperCase()); // Access member
}
}
可以简化为:
void outputValue(Object obj) {
if (obj instanceof String aString) { // Compare and cast (if true)
System.out.println(aString.toUpperCase()); // Access member
}
}
参考: https : //jaxenter.com/pattern-matching-for-instanceof-in-java-14-169830.html
例如,如果您将 p1 作为 Object 类型移交,编译器将不知道它实际上是 Square 的一个实例,因此将无法访问方法等。 if 只是检查某种类型以返回真/假,但这不会改变变量 p1 的类型。
变量 p1 具有它开头的任何类型 - 让我们说 Shape。 p1 是一个Shape,并且只是一个Shape,不管它当前的内容是否恰好是一个Square。 您可以在 Square 上调用(比方说) side(),但不能在 Shape 上调用。 只要您通过变量 p1(其类型为 Shape)识别有问题的实体,就不能对其调用 side(),因为变量的类型。 Java 的类型系统的工作方式是,如果您在碰巧知道它是 Square 时可以调用 p1.side(),则始终可以调用 p1.side()。 但是 p1 不仅可以容纳方形,还可以容纳(比如说)圆形,当 p1 持有圆形时调用 p1.side() 将是错误的。 所以你需要另一个变量来表示你碰巧知道是 Square 的 Shape,一个类型为 Square 的变量。 这就是为什么演员是必要的。
正如 Leroy 所提到的,Java 14 为 instanceof 引入了模式匹配。 因此,您可以将 instanceof 检查和 typecast 结合在一个表达式中:
if (p1 instanceof Square) { c1 = (Square) p1; }
if (p1 instanceof Square) { c1 = (Square) p1; }
可以改写为
if (p1 instanceof Square c1) { // use c1 }
此功能在 Java 16 (JEP 394) 中完成。 对于以下版本,请参阅此链接以从 IntelliJ、Eclipse 和 STS 等 IDE 启用此预览功能。
如果将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”等,也就是他用class作为类型标志。 最好只有一个类并为各种类型添加一个枚举。 但也有很多非常好的用途。 也许最明显的是:
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。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.