Java中具有不同参数数据类型的继承和重载方法

Java中具有不同参数数据类型的继承和重载方法,java,inheritance,polymorphism,overloading,overriding,Java,Inheritance,Polymorphism,Overloading,Overriding,当我分析一个与重载和继承相关的简单java代码时,我希望收到一个重载匹配参数数据类型的输出。但不是这样的 代码: 输出: num1 Type: java.lang.Integer num2 Type: java.lang.Long a1 Type: D a2 Type: D calc A calc A Number: 22 我在这里测试代码: 您的主要问题似乎是为什么类型输出与正式类型不匹配。这完全是有意为之,这就是为什么面向对象编程如此强大的原因 在实例上调用方法时,运行时系统会查看实例的实

当我分析一个与重载和继承相关的简单java代码时,我希望收到一个重载匹配参数数据类型的输出。但不是这样的

代码:

输出:

num1 Type: java.lang.Integer
num2 Type: java.lang.Long
a1 Type: D
a2 Type: D
calc A
calc A
Number: 22
我在这里测试代码:

您的主要问题似乎是为什么类型输出与正式类型不匹配。这完全是有意为之,这就是为什么面向对象编程如此强大的原因

在实例上调用方法时,运行时系统会查看实例的实际类型,并根据其实际类型而不是形式类型查找要调用的方法

如果不是这样,你将无法完成任何有用的事情。您希望能够声明一个抽象类
A
,具体类
B
C
以不同的方式实现细节。但是您还希望能够声明
A
类型的变量,而不必关心它们来自何处,以及它们实际上是
B
类型还是
C
类型。然后,您可以调用作为
A
契约一部分的方法,它将做正确的事情:真正的
B
将调用
B
的实现,同样地,对于
C

至于为什么最终调用
A
calc
方法而不是
D
,这也是因为多态性的工作方式。变量的形式类型是
A
;因此,当您调用
.calc()
时,类型系统将:

  • 在类
    A
    中找到最合适的方法来匹配编译时的调用
  • 查看是否已在运行时在
    A
    和实际类型之间重写该属性
  • 如果有覆盖版本,则调用覆盖版本;如果没有,则调用
    A
    的版本
  • 但是您根本没有重写
    calc()
    方法:您提供了具有不同签名的方法。因此,在步骤1(编译时)中,类型系统找到
    A.calc(double)
    ;在步骤2中(在运行时),它发现这并没有在类层次结构的更底层被覆盖;因此,在步骤3(运行时)中,它调用
    A
    的版本


    重载在编译时根据形式类型解决;重写在运行时根据实际类型进行解析。

    这是因为这些方法是重载,而不是原始
    calc
    方法的重写。因此,如果您使用的是
    a
    类型的引用,则可以看到的都是最初属于
    a
    的方法。所有其他方法都隐藏在对象中,就像您用新名称编写它们一样

    因此,当编译器必须决定为每次计算调用哪个方法时,它并没有您认为它拥有的所有选项。它只有原始的
    calc(double)
    ,因此它将调用编译为“将值转换为double并调用
    calc(double)
    ”。在编译时,它不知道实际的类不是
    A
    。它不能编译成这样的代码:“在运行时检查是否有一个名为
    calc(int)
    ,如果有,就使用它,如果没有,就转换成double并使用
    calc(double)
    。它需要知道在编译时放在那里的指令是什么。那时,它所知道的关于这个引用的所有信息都是
    a

    根据评论进行编辑

    编译器总是使用引用类型的约定来选择要调用的方法。也就是说,变量的类型,在本例中是A

    无论实际对象是否具有重写方法,都会发生这种情况。此时,编译器不知道它。它所做的是告诉运行时环境:“当到达这一点时,获取实际对象,并使用此签名运行方法:
    calc(double)

    因此,如果在运行时,实际对象也有
    calc(int)
    calc(long)
    以及其他名为
    calc
    的方法,这并不重要,因为编译器说“使用
    calc(double)

    现在,如果运行时对象有一个重写
    calc(double)
    ,则运行时环境将采用它,而不是原始的
    calc(double)
    ,因为这是重写的本质

    总而言之:

  • 编译器只知道引用类型(在本例中是您的变量)中存在的方法签名
  • 编译器放置的指令意味着“使用具有此特定签名或任何重写(具有相同签名)的方法”
  • 运行时环境查看实际对象,并检查它有什么类型的
    calc(double)
    。如果它有覆盖,它将使用该覆盖。如果它只有原件,它将使用它

  • 我认为调用这样一个重载方法,首先将调用实际的类方法(在本例中为D)(不是变量->A中的一个),如果不匹配,契约将转到父类,依此类推。。。在这段代码中就是这样工作的:@carvallo在您链接到的代码中,这是一个覆盖,而不是重载。覆盖具有相同的名称和相同的参数列表;重载具有相同的名称,但参数列表不同。实际上,重载方法是一种完全不同的方法。好吧,我知道第二个例子是重写。。。所以,只是为了检查我是否理解得很好:在重写中,VM选择与对象(而不是变量)契约匹配的方法(在运行时),在重载中,编译器根据调用它的变量的类型选择方法。如果它是这样工作的,那么让我困惑的是,在我的示例中,当我检查两种情况下的对象类型时,它都返回D,但存储
    num1 Type: java.lang.Integer
    num2 Type: java.lang.Long
    a1 Type: D
    a2 Type: D
    calc A
    calc A
    Number: 22