【问题标题】:Why the method that gets the Father class as a parameter is called and not the method that gets the child class as a parameter?为什么调用获取父类作为参数的方法而不是获取子类作为参数的方法?
【发布时间】:2019-02-06 11:01:31
【问题描述】:

我有一个名为 A 的类,以及一个扩展 A 的名为 B 的类。 使用一些方法来理解多态行为,我遇到了一个奇怪的情况。

public class Main {
    public static void main(String[] args){
        B b = new B();
        A a = b;
        b.f1(a);
    }
}

public class A {
.
.
.
    public void f1(A a){
        if(a instanceof B)
            f1((B)a);
        else
            System.out.println("Nothing");
    }
.
.
.
}

public class B extends A {
.
.
.
    public void f1(B b){
        System.out.println("B::f1(B)");
    }
.
.
.
}

我希望首先调用 A 类中的 f1(因为 a 属于 A 类型),这实际上发生了。然后我期待这条线 f1((B)a);被调用,因为 a 是 B 的一个实例。到目前为止,一切都按预期进行。但是,我认为下一个将被调用的方法是 B 类中的 f1(B)。相反,A 类中的 f1(A) 被一遍又一遍地调用,导致堆栈溢出异常。为什么不调用 B 类中的 f1(B)? B 的一个实例是调用者,参数被强制转换为 B 类型。

【问题讨论】:

标签: java oop inheritance polymorphism dynamic-binding


【解决方案1】:

f1(A a) 是类A 的实例方法。它不知道A 的子类的方法。因此,它不能调用类Bvoid f1(B b)。因此f1((B)a)再次执行void f1(A a)

如果你想调用f1(B b),你必须在B类的实例变量上调用f1

public void f1(A a){
    if(a instanceof B) {
        B b = (B)a;
        b.f1(b);
    } else {
        System.out.println("Nothing");
    }
}

【讨论】:

  • 哦,我明白了。反过来呢,我能不能从 B 类写的方法调用 A 类写的方法(当然前提是 B 类没有重写该方法)?
  • @RonenHanukayev 是的,因为 B 可以看到其超类 A 的所有(公共或受保护)方法。
  • @Eran 请注意我的回答。这样做会产生 ClassCastException,因此应告知调查问卷。
  • @DavideLorenzoMARINO 如果在进行任何转换之前检查 a instanceof B,如何抛出 ClassCastException?
  • 哦,我明白了...您没有强制转换“this”,但您在参数实例上调用了 f1。有趣的把戏。 +1
【解决方案2】:

您的 A 类不知道 B 类存在于某处并且具有 B.f1(B b) 功能。实际上 f1(A a) 和 f1(B b) 是两个不同的函数。您可能想要实现的目标应该以不同的方式完成:

public class A {
//...
    public void f1(A a) {
         System.out.println("Nothing");
    }
//...
}

public class B {
//...
    public void f1(B a) {
        // this is different function, because of another parameters
    }

    public void f1(A a) {
        if(a instanceof B)
            f1((B)a);
        else
            super.f1(a);
    }
//...
}

【讨论】:

    【解决方案3】:

    代码必须按如下方式转换方法的调用者:

    public class A {
    
        public void f1(A a){
            if(a instanceof B)
                ((B) this).f1(a);
            else
                System.out.println("Nothing");
        }
    }
    

    在您的代码中,您只转换参数。这样做会导致递归调用同一类的同一方法。

    如果你转换调用者,你会通知 JVM 你想调用子类上的方法,所以你可以立即退出该方法。

    重要提示请注意,根据参数对调用者调用强制转换可能会生成类强制转换异常,例如在以下上下文中:

        B b = new B();
        A a = new A();
        a.f1(b);
    

    无法确定接收 B 参数的方法 f1 是否在 B 对象上调用

    【讨论】:

    • 可能((B) this).f1((B)a); 是作者需要的,因为f1(B b)B 类型的参数所期望的。我认为检查this 实际上也可以转换为B 会很棒:)
    猜你喜欢
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 2013-11-30
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    相关资源
    最近更新 更多