子类型和超类型、方法和转换

Sub- and Supertypes, Methods, and Conversion

我目前正在阅读 Barbara Liskov 相当不错的第二版 程序开发 Java:抽象、规范和面向对象设计 (2000)研究生课程。作为参考,对于这个问题,我正在查阅第 2.4.2 节:转换和重载(第 27-29 页)。 Liskov 正在讨论方法重载以及在为重载调用提供实际参数时 Liskov 所说的编译器对“'most specific'”(第 28 页)方法的识别。

下面是 Liskov 提供的重载方法的例子 foo:

void foo (T a, int x) // defn. 1
void foo (S b, long y) // defn. 2

下面的调用 - Liskov 指出不合法

o.foo(e, 3)

以下是我们对实际参数的了解:

  1. S 是 T 的子类型。
  2. 参数eS
  3. 类型的变量

在我看来,这在理论上是合法的。调用 o.foo(e, 3) 与使调用更具体相同(如果我在比较明显类型时像编译器一样思考):

o.foo((T) e, 3)

显然,e 在进行此调用时只能访问超类型的简化的、常见的行为;不过,这不还是合法的电话吗?

规模较小:

Object o = s 是合法的,其中 s 计算为类型 String(也只是为了引用:Liskov,第 26 页)。

为什么我错了?我有什么不明白的?

所以,我向我的一个计算机科学伙伴展示了这个问题。而且,现在我知道了答案,我觉得自己像个傻瓜(他问我这个问题,"Yeah, it's legal; but, how does the compiler know which method to call?");然而 - 既然我已经度过了我的 'Oh, duh!' 时刻 - 我认为答案仍然对那些暂时陷入与我相同的位置的人有帮助:

这里,问题不是调用的合法性(如果//defn. 1是唯一可用的方法,调用将是绝对合法的)。这是一个问题 - 如上面的问题所述 - "most specific" 而对象 e 不是问题。

类型int的实际参数3是问题所在。

为什么? int 合法扩展为类型 long;因此,编译器不知道调用哪个方法,因为两个调用都可以接收这些实际参数。