Java - 调用方法时的继承和变量类型

时间:2016-07-17 04:05:47

标签: java inheritance methods override invoke

我对某些代码的结果有一些大问题。

public class ClassA {
    public void stampa(ClassA p) {
        System.out.println("AAA");
    }
}
public class ClassB extends ClassA {
    public void stampa(ClassB p) {
        System.out.println("BBB");
    }
    public void stampa(ClassA p) {
        System.out.println("AAA/BBB");
    }
}
public class ClassC extends ClassA {
    public void stampa(ClassC p) {
        System.out.println("CCC");
    }
    public void stampa(ClassA p) {
        System.out.println("AAA/CCC");
    }
}

主要看起来像这样

public static void main(String[] args) {
    ClassA a1, a2;
    ClassB b1;
    ClassC c1;
    a1 = new ClassB();
    b1 = new ClassB();
    c1 = new ClassC();
    a2 = new ClassC();
    b1.stampa(b1);//BBB
    a1.stampa(b1);//AAA/BBB
    b1.stampa(c1);//AAA/BBB
    c1.stampa(c1);//CCC
    c1.stampa(a1);//AAA/CCC
    a2.stampa(c1);//AAA/CCC
}

我很难理解为什么a1.stampa(b1)的结果;是" AAA / BBB"而不是" BBB"。 正如我从继承中理解的那样,编译时a1的静态类型是ClassB所以我搜索了" stampa" ClassB中的方法,对于方法的参数,静态类型也是ClassB,所以我将选择ClassB的第一个stampa方法。

当我试图理解a2.stampa(c1)的结果时也会发生同样的事情;这是" AAA / CCC"而不是" CCC"。

有人可以帮我理解我做错了吗?

5 个答案:

答案 0 :(得分:1)

问题在于以下

public class ClassB extends ClassA {
    public void stampa(ClassB p) {

不是覆盖

public class ClassA {
    public void stampa(ClassA p) {

重载而不是......

这意味着它是一个额外的方法,而不是覆盖,只能通过b1.stampa(b1);访问。

如果您具有相同的方法签名,则仅覆盖发生,除了名为' co-variant返回',基本上相同的概念,但返回类型是子类

答案 1 :(得分:0)

这是一个与重载和多态相关的问题。

ClassA a1;
ClassB b1;
...
a1.stampa(b1);

编译器只能确定两个参数类型,并在编译时调用函数名称(因为多态性)。

所以“a1.stampa(b1);” make class在ClassA中使用参数类型ClassB查找stampa,但是ClassA没有这个函数,所以参数降级,在ClassA中编译参数类型为ClassA的find stampa。这个函数是存在的,所以这一行中的编译器记录了一个名为stampa和参数类型为ClassA的函数应该在这里调用。

当程序运行时,a1实际上是一个ClassB对象,该对象用名称stampa和参数类型ClassA覆盖该函数。因此,最终输出为“AAA / BBB”。

只记得在编译时发生重载,并且在执行时发生多态。

请忽略我破碎的英语。

答案 2 :(得分:0)

这是关于覆盖重载方法之间的区别。

方法有signature,由方法名称和参数类型定义,但不是参数名称或返回类型。

只要签名不同,您就可以在具有相同名称的类上定义多个方法。这称为重载

如果子类定义了与相同签名的方法作为超类,则子类覆盖超类方法。这意味着无论用于进行调用的变量类型如何,都将调用子类方法。

Java编译器确定在编译时调用哪个方法,即要使用的方法签名。要调用该签名的实际方法是在运行时确定的,具体取决于引用对象的实际类。

因此,在编译时,对于a1.stampa(b1),编译器会查看类stampa上的所有ClassA(因为a1是键入ClassA),只找到一个这样的方法,签名为stampa(ClassA)

运行时a1引用的对象实际上是ClassB,因此ClassB.stampa(ClassA)被调用,因为ClassBClassA覆盖了该方法。

ClassB.stampa(ClassA)然后打印AAA/BBB

答案 3 :(得分:0)

据我所知,a1.stampa(b1)的结果;是“AAA / BBB”而不是“BBB”,因为当你将b1传递给stampa()时,它将被类型转换为超类引用,因为你有重载的超类方法,而重载方法的执行总是子类方法得到偏好所以子类执行方法而不是超类。

答案 4 :(得分:0)

这是运行时多态性,这是通过重写方法实现的。 首先,你甚至不能指望a1.stampa(b1);调用一个不覆盖父类方法的方法(在你的情况下碰巧是public void stampa(ClassB))。这个方法永远不会从你的调用调用它不会覆盖父类方法。

现在调用public void stampa(ClassA p)的原因是因为在运行时父类引用变量a1引用ClassB的对象。所以,覆盖父类方法的方法被称为。

您的其他问题也是如此。

为了更好地理解运行时多态性,您可以参考https://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html