为什么在一个instanceOf后施放?

时间:2010-11-15 16:12:41

标签: java polymorphism instanceof

在下面的示例中(来自我的coursepack),我们希望向Square实例c1提供其他对象p1的引用,但前提是这两个对象是兼容的类型。

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

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

我怀疑答案在于明显和实际类型之间的区别,但我仍然感到困惑......

修改
编译器将如何处理:

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

EDIT2:
instanceof是否检查实际类型而非明显类型的问题是什么?那么演员会改变明显的类型?

谢谢,

JDelage

11 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:10)

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

Object p1 = new Square();
Square c1;

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

答案 2 :(得分:5)

测量一个物体是否适合一个盒子,并且实际放入盒子中之间存在差异。 instanceof是前者,铸造是后者。

答案 3 :(得分:3)

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

答案 4 :(得分:3)

旧代码无法正常工作

impield cast功能毕竟是合理的,但由于向后兼容性,我们无法将此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 :(得分:1)

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

答案 6 :(得分:1)

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

答案 7 :(得分:1)

只需对此进行更新,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

答案 8 :(得分:0)

如果将c1声明为Square类型,则需要进行投射。如果声明为Object,则不需要进行投射。

答案 9 :(得分: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,那么您无需进行测试。但是把它留给私人方法......

答案 10 :(得分: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。但是后来甚至没有办法用通用的对象来调用它,在许多情况下这可能是非常需要的。实际上,也许我想要一个集合,比如包括字符串和整数,我希望比较不同类型只是返回false。