为什么要在instanceOf之后进行转换?

问题描述 投票:25回答:11

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

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

我在这里不明白的是,我们首先检查p1确实是Square,然后我们仍将其强制转换。如果是Square,为什么要强制转换?

我怀疑答案在于表观类型和实际类型之间的区别,但是我还是很困惑...

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

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

Edit2:instanceof检查actual类型而不是apparent类型的问题吗?然后强制转换更改apparent类型?

谢谢,

JDelage

java polymorphism instanceof
11个回答
13
投票

请记住,您始终可以将Square的实例分配给继承链更高一级的类型。然后,您可能需要将不太具体的类型转换为更具体的类型,在这种情况下,您需要确保您的转换有效:

Object p1 = new Square();
Square c1;

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

12
投票

编译器不会推断出由于您位于块中,因此您已经成功检查了对象的类型。仍然需要显式强制转换,以告知编译器您希望将对象引用为其他类型。

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


6
投票

旧代码无法正常工作

impield cast feature毕竟是合理的,但是由于向后兼容,我们很难将此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,它将使用第一个方法进行编译!


5
投票

[测量某个对象是否适合放入盒子与实际之间有区别。 instanceof是前者,而投射是后者。


5
投票
因为此特定syntactic sugar尚未添加到语言中。我认为它是为Java 7提出的,但似乎没有输入project coin

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

1
投票
变量p1的开头是任何类型-假设为Shape。 p1是一个Shape,并且仅仅是Shape,无论其当前内容恰好是Square。您可以调用-假设-在Square上调用side(),但不能在Shape上调用。只要您通过类型为Shape的变量p1来识别所讨论的实体,由于变量的类型,就不能在其上调用side()。 Java类型系统的工作方式,如果您碰巧知道它是一个Square时可以调用p1.side(),则可以

always调用p1.side()。但是p1不仅可以容纳正方形,而且可以(例如)容纳圆形,并且当p1容纳一个圆形时调用p1.side()是错误的。因此,您需要另一个变量来表示您碰巧知道的Shape,即Square,即类型为Square的变量。这就是为什么必须进行强制转换。


0
投票
如果将c1声明为Square类型,则需要强制转换。如果将其声明为Object,则不需要强制转换。

0
投票
已完成测试以防止在运行时出现ClassCastExceptions

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

如果您完全肯定p1Square,则无需测试。但这留给私有方法...    

0
投票
不必令人讨厌,但您必须告诉编译器您想做什么,因为替代方法是让它猜测您要做什么。当然,您可能会想,“如果我正在检查对象的类型,显然这意味着我想将其强制转换为该类型。”但是谁说呢?也许那是您要做什么,也许不是。

当然,在简单的情况下

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应该采用哪种类型?

RE认为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。但是,甚至没有办法用通用对象来调用它,这在很多情况下是非常可取的。确实,也许我想让一个集合同时包含Strings和Integers,并且我希望比较不同类型只是返回false。    

0
投票
仅对此提供更新,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    
© www.soinside.com 2019 - 2024. All rights reserved.