27

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

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

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

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

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

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

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

4

12 回答 12

13

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:

Object p1 = new Square();
Square c1;

if(p1 instanceof Square)
    c1 = (Square) p1;
于 2010-11-15T16:15:44.020 回答
12

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.

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;
}

In C# you can do the check and the cast in 1 call:

c1 = p1 as Square;

This will cast p1 to a Square, and if the cast fails, c1 will be set to null.

于 2010-11-15T16:15:20.793 回答
12

旧代码将无法正常工作

隐含的强制转换特性毕竟是合理的,但由于向后兼容性,我们很难将这个 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

我们最终在 JDK 14 中实现了这个功能。您可能已经注意到,您可以在 instanceof-linkage 中声明一个新变量。这个新变量已由自动向上转换为指定类型的值定义。

if (any instanceof String s) {
  System.out.println(s);
}
于 2015-08-09T09:28:06.527 回答
5

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

于 2010-11-15T16:27:17.497 回答
4

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

于 2010-11-15T16:15:18.950 回答
2

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

于 2020-05-29T10:54:37.827 回答
1

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.

于 2010-11-15T16:16:58.933 回答
1

变量 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的变量。那'

于 2010-11-15T16:26:02.900 回答
1

正如 Leroy 提到的,Java 14 为 instanceof 引入了模式匹配。因此,您可以将 instanceof 检查和类型转换完全结合在一个表达式中:

if (p1 instanceof Square) { c1 = (Square) p1; }可以改写为

if (p1 instanceof Square c1) { // use c1 }

此功能已在 Java 16 (JEP 394) 中完成。对于以下版本,请参阅链接以从 IntelliJ、Eclipse 和 STS 等 IDE 启用此预览功能。

于 2021-10-26T09:11:21.213 回答
0

If c1 is declared as a type of Square then casting is required. If it is a declared as an Object then casting is not needed.

于 2010-11-15T16:15:06.467 回答
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 假设什么类型?

重新评论 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。

于 2010-11-15T17:59:49.647 回答
0

测试是为了防止ClassCastExceptions在运行时:

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

如果您绝对肯定p1是 a Square,那么您不必进行测试。但是把它留给私有方法......

于 2010-11-15T16:17:44.703 回答