简体   繁体   English

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

[英]Inheritance and Overloading methods with different argument data types in Java

When I was analyzing a simple java code related with overloading and inheritance I expected to recieve an output that overloads matching the argument's data types. 当我分析与重载和继承相关的简单Java代码时,我希望收到与参数的数据类型匹配的重载输出。 But it doesn't work that way. 但这不是那样的。

Code: 码:

class A {
        public int calc (double num){
            System.out.println("calc A");
            return (int)(num+1);}
    }
class B extends A{
        public int calc (long num){
            System.out.println("calc B");
            return (int)(num+2);}
    }
class C extends B{
        public int calc (int num){
            System.out.println("calc C");
            return num+3;}
    }
class D extends C{
        public int calc (float num){
            System.out.println("calc D");
            return (int)(num+4);}
    }

class Program{
        public static void main(String[] args){
            int num1=10;
            long num2 = num1;

            Object o1 = num1;
            System.out.println("num1 Type: "+o1.getClass().getName());

            Object o2 = num2;
            System.out.println("num2 Type: "+o2.getClass().getName());

            A a1=new D();
            A a2=new D();

            System.out.println("a1 Type: "+a1.getClass().getName());
            System.out.println("a2 Type: "+a2.getClass().getName());

            int result = a1.calc(num1)+a2.calc(num2);
            System.out.println("Number: "+result);
        }
    }

Output: 输出:

num1 Type: java.lang.Integer
num2 Type: java.lang.Long
a1 Type: D
a2 Type: D
calc A
calc A
Number: 22

I was testing the code here: ideone 我在这里测试代码: ideone

Your main question seems to be about why the type outputs don't match with the formal types. 您的主要问题似乎是关于为什么类型输出与形式类型不匹配。 This is entirely intentional, and it's what makes object oriented programming so powerful. 这完全是故意的,这就是使面向对象编程如此强大的原因。

When a method is invoked on an instance, the runtime system looks at the actual type of the instance, and looks up the method to call based on its actual type, rather than on its formal type. 在实例上调用方法时,运行时系统将查看实例的实际类型,并根据其实际类型而不是形式类型来查找要调用的方法。

If this weren't the case, you wouldn't be able to get anything useful done. 如果不是这种情况,您将无法完成任何有用的工作。 You want to be able to declare an abstract class A , with concrete classes B and C hanging off it that implement the details in different ways. 您希望能够声明一个抽象类A ,而具体的类BC则以抽象类A挂起,它们以不同的方式实现了细节。 But you also want to be able to declare variables of type A , without caring where they've come from, and whether they're actually of type B or type C . 但是您还希望能够声明A类型A变量,而不必关心它们的来源以及它们实际上是B类型还是C类型。 You can then invoke methods that are part of the contract of A , and it'll do the right thing: something that's really a B will invoke B 's implementation, and likewise for C . 然后,您可以调用属于A契约的一部分的方法,它将做正确的事情:实际上是B东西将调用B的实现,对于C也是如此。

As for why you end up invoking A 's calc method rather than D 's, this is again because of the way polymorphism works. 至于为什么最终调用Acalc方法而不是D的原因,这又是由于多态性的工作方式。 The formal type of the variables is A ; 变量的形式类型为A ; so when you invoke .calc() , the type system will: 因此,当您调用.calc() ,类型系统将:

  1. find the most appropriate method in class A to match the call at compile time ; A类中找到最合适的方法来匹配编译时的调用;
  2. see whether that has been overridden between A and the actual type at runtime ; 查看在运行时是否已在A和实际类型之间覆盖了它;
  3. call the overridden version if there is one, or A 's version if not. 如果存在,则调用覆盖的版本;如果没有,则调用A的版本。

But you haven't overridden the calc() method at all: you've supplied methods with different signatures. 但是,您根本没有覆盖calc()方法:您提供了具有不同签名的方法。 So in step 1 (at compile time) the type system finds A.calc(double) ; 因此,在步骤1(在编译时)中,类型系统找到A.calc(double) in step 2 (at runtime) it discovers that this hasn't been overridden further down the class hierarchy; 在第2步中(在运行时),它发现在类层次结构中没有进一步覆盖它; in step 3 (runtime) it therefore invokes A 's version. 因此,在第3步(运行时)中,它将调用A的版本。

Overloads are resolved at compile time based on formal types; 重载是在编译时根据形式类型解决的; overrides are resolved at runtime based on actual types. 覆盖将在运行时根据实际类型进行解析。

This is because those methods are overloads , not overrides of the original calc method. 这是因为这些方法是重载 ,而不是原始calc方法的替代 Therefore, if you are using a reference of type A , all that can be seen are methods that originally belonged to A . 因此,如果您使用类型A的引用,则所有可见的都是最初属于A All the other methods are hidden in the object, just as if you had written them with new names. 所有其他方法都隐藏在对象中,就像您用新名称编写它们一样。

So when the compiler has to decide which method to call for each calculation, it doesn't have all the options that you think it has. 因此,当编译器必须决定为每个计算调用哪种方法时,它并没有您认为具有的所有选项。 It just has the original calc(double) , so it compiles the call as "convert the value to double and call calc(double) ". 它只具有原始的calc(double) ,因此它将调用编译为“将值转换为double并调用calc(double) ”。 At compile time, it doesn't know that the actual class is not A . 在编译时,它不知道实际的类不是A It can't compile into code that says "check at runtime if there is a method called calc(int) , if so, use it, if not, convert to double and use calc(double) . It needs to know what instructions to put there at compile time. And at that time, all it knows about this reference is that it's an A . 它不能编译成这样的代码:“在运行时检查是否有一个称为calc(int)的方法,如果有,请使用它,如果没有,请转换为double并使用calc(double) 。它需要知道要执行哪些指令。放在编译时。那时,它对该引用的全部了解是它是A

EDIT in response to comments : 编辑以回应评论

The compiler always chooses which method is going to be invoked using the contract of the reference's type . 编译器始终使用引用类型的协定来选择要调用的方法。 That is, the type of your variable, which is A in this case. 也就是说,变量的类型,在这种情况下为A。

This happens whether or not the actual object has an overriding method. 无论实际对象是否具有覆盖方法,都会发生这种情况。 At this point the compiler doesn't know about it. 在这一点上,编译器对此一无所知。 What it does is tell the runtime environment: "When you get to this point, take the actual object, and run the method with this signature: calc(double) ". 它的作用是告诉运行时环境:“到此为止,请获取实际对象,并使用以下签名运行方法: calc(double) ”。

So, if at runtime, the actual object has also calc(int) and calc(long) and other methods named calc , it doesn't matter, because the compiler said "use calc(double) ". 因此,如果在运行时实际对象还具有calc(int)calc(long)以及其他名为calc方法,则没有关系,因为编译器说“ use calc(double) ”。

Now, if the runtime object has an overriding calc(double) , the runtime environment will take that instead of the original calc(double) because that's the nature of overriding. 现在,如果运行时对象具有覆盖的 calc(double) ,则运行时环境将使用该值而不是原始的calc(double)因为这是覆盖的本质。

To sum up: 总结一下:

  1. The compiler only knows about method signatures that exist in the reference type - your variable, in this case. 编译器仅知道引用类型中存在的方法签名-在这种情况下为变量。
  2. The compiler puts instructions that mean "use the method with this specific signature or any override (with the same signature). 编译器放置的指令的意思是“使用具有此特定签名或任何替代(具有相同签名)的方法。
  3. The runtime environment looks at the actual object, and checks what kind of calc(double) it has. 运行时环境查看实际对象,并检查其具有哪种calc(double) If it has an override, it will use that. 如果有覆盖,它将使用该覆盖。 If it has only the original, it will use that. 如果只有原始文件,它将使用该文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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