为什么需要铸造

问题描述 投票:0回答:2

按预期进行以下工作:

public boolean equals(Object otherObject){
        if (otherObject instanceof Employee){
            Employee toCompare = (Employee) otherObject;
            if(this.getLastName().equals(toCompare.getLastName()) && this.idNumber == toCompare.idNumber) return true;
        }
        return false;
    } 

我的问题是为什么这也不起作用:

public boolean equals(Object otherObject){
        if (otherObject instanceof Employee){            
            if(this.getLastName().equals(otherObject.getLastName()) && 
               this.idNumber == otherObject.idNumber) return true;
        }
        return false;
    }  

他们将其排除在编译器之外的原因是什么?或者,toCompare 有哪些 otherObject 没有的?他们都指向同一个位置,所以有什么区别?

java casting compiler-construction
2个回答
3
投票

您可以访问变量的字段和方法取决于它的静态类型。

otherObject
Object
类型,因此您不能调用特定于
Employee
的方法。

instanceof
的模式匹配使得这更容易编写,虽然。

if (otherObject instanceof Employee emp) {
    return this.getLastName().equals(emp.getLastName()) && 
           this.idNumber == emp.idNumber;
}

0
投票

你说的就是所谓的流动式收紧。你想要这个:

Object foo = ...;
if (foo instanceof String) {
  foo.toLowerCase();
}

工作是因为特别是在

if
块的范围内,
foo
肯定是一个字符串。

这个想法有一些基本问题:

  • 假设
    foo
    不是局部变量而是字段。当然这应该仍然有效。除了...字段可以由其他线程即时更改。那么语言是否应该 not 将此原则应用于字段,而 do 将其应用于方法局部变量?这听起来像是你只是在为其他人打开大门,问一个关于为什么在大火中存在差异的令人难以置信的问题。
  • 这意味着如果您随后在
    foo
    中更改
    if
    ,事情就会变得有点奇怪。这里应该发生什么:
Object foo = ...;
if (foo instanceof String) {
  if (Math.random() < 0.00005) foo = 5;
  foo.toLowerCase();
}

这显然不应该起作用 - 当您调用

foo
时,
foo.toLowerCase()
不是字符串的可能性不大,但有可能。所以在这里它行不通?

希望您能看到过度推理的普遍问题。当您编写一些代码并且它没有按照您想要的方式执行时,它会使尝试调试情况变得异常复杂。它使编译器极其复杂。

现在,这并不意味着添加它是个坏主意。这只是对更简单得多的事情的解释:

  • JDK的工程师在java 1.0发布的时候是这样设置的可以理解

就算你觉得不对,那也不是重点。关键是选择不使用流式结构是可以理解的。

这让我们进入下一点:

...现在我们被困住了

引入流类型now破坏向后兼容性。奇怪,但确实如此 - 这是因为在 Java 中,方法参数是方法标识的一部分,因此,更改类型可以使完全相同的代码现在引用完全不同的方法。这是当前的 vanilla java,如果您愿意,请尝试一下:

class Test {
  public static void main(String[] args) {
    Object a = "foo";
    String b = "foo";
    System.out.println(test(a));
    System.out.println(test(b));
  }

  public static void test(Object x) {
    System.out.println("foo(Object)");
  }

  public static void test(String x) {
    System.out.println("foo(String)");
  }
}

这将打印

foo(Object)
,然后是
foo(String)
。您可能会觉得很奇怪——在这两种情况下,传递的对象都是 String 类型。这是因为使用这两种方法中的哪一种是由编译器决定的,而不是在运行时决定的。 Java 确实有动态类型,但它仅适用于重写方法时,这不仅要求名称相同,而且参数类型也相同。这里不是这种情况。

我们可以再深入探讨为什么有人会设计这样一种语言,但在某个时候,我们会在这里,从现在起 4871 年,你会问“sooo,关于那个大爆炸”。这当然远远超出了这个问题的范围。

无论如何,考虑到这就是 java 的工作方式,添加流类型会破坏事情。毕竟,想象一下我在上面的代码中所做的:

Object q = "Hello";
if (q instanceof String) {
  test(q);
}

然后当前的 java 将打印

test(Object)
而如果你添加流类型,你会得到
test(String)
并且这是不向后兼容的。

好消息

仅仅因为其他语言是这样做的并不意味着 java 必须遵循和打破东西,也不意味着 java 只能坐在那里后悔。

您只需添加不同的功能。这就是模式的用武之地。新的 Java 功能比这 非常 更复杂,但它可以做的许多事情之一是:

Object foo = "x";
if (foo instanceof String bar) {
  bar.toLowerCase();
}

这很好用,并且避免了向后不兼容的问题。它还避免了关于“..但是如果

foo
是一个在中途更改的字段怎么办(无关紧要;对变量
bar
的测试和赋值是原子的)”的混淆,它避免了重新分配它的问题(随意,但是变量
foo
Object
类型,变量
bar
String
类型,没有什么可以改变这一点,所以那里也没有混淆)。

它还允许解构、模式匹配等等。

如果您想了解更多,可以在 openjdk 邮件列表上以极长的篇幅讨论这些内容(如果您想了解所有细节,您将花费数周时间阅读)。 valhalla-dev、amber-dev、core-lib-dev 可能对此很有趣。这是amber-dev邮件列表的主页。

© www.soinside.com 2019 - 2024. All rights reserved.