繁体   English   中英

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

[英]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

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

if (any instanceof String s) {
  System.out.println(s);
}

测量某个物体是否适合放在盒子中与实际其放入盒子中是有区别的。 instanceof是前者,而casting 是后者。

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

只是为了提供更新,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
}

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

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

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

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM