有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如何知道在Java上的extends中执行的方法?

这是我的A班<;-B<;-C

class A {   
    void f ( A a ) { System . out . println ( " AA " );}
    void f ( C c ) { System . out . println ( " AC " );}
}
class B extends A {
    void f ( B b ) { System . out . println ( " BB " );}
}
class C extends B {
    void f ( A a ) { System . out . println ( " CA " );}
    void f ( C c ) { System . out . println ( " CC " );}
}

当我用流动代码实现main时,我不理解结果

public class app {
    public static void main ( String [] args ) {
        A aa = new A ();
        B bb = new B ();
        A ac = new C ();
        A ab = new B ();
        B bc = new C ();            
        C cc = new C ();

        aa.f ( ac );
        bb.f ( ac );
        ab.f ( bb );
        bb.f ( ac );
        bb.f ( cc );

    }
}

结果是

 AA 
 AA 
 AA 
 AA 
 CA //Edit : AC instead of CA

我不明白Java是如何找到这些结果的

请问有人能解释一下结果吗


共 (1) 个答案

  1. # 1 楼答案

    方法概述

    让我们一步一步来看看你的例子。您有三个类,如果我们列出它们的所有方法(包括重载和继承的方法),那么您有:

    class A:
        void f (A a) -> prints "AA" // #1
        void f (C c) -> prints "AC" // #2
    
    class B:
        // from A
        void f (A a) -> prints "AA" // #3
        void f (C c) -> prints "AC" // #4
    
        // from B, overload
        void f (B b) -> prints "BB" // #5
    
    class C:
        // from B
        void f (B b) -> prints "BB" // #6
    
        // from C, overrides methods from A
        void f (A a) -> prints "CA" // #7
        void f (C c) -> prints "CC" // #8
    

    因此B类定义了一个新的重载的变量,它接受BC类继承了AB的所有内容,覆盖了A的两个方法


    解释

    现在开始测试:

    aa.f(ac); // AA
    bb.f(ac); // AA
    ab.f(bb); // AA
    bb.f(ac); // AA
    bb.f(cc); // CA
    

    您的命名约定意味着实际实例是右字符类型,而您通过左字符类型的变量引用它。因此ab实际上是类B的一个实例,并由允许引用A类型的变量引用

    知道了这一点,就很容易理解结果。第一个例子:

    aa.f(ac); // AA
    

    我们有一个类型为A的实例,在类型为A的变量中。参数的类型为C,变量的类型为A。非常重要的是,请注意变量的类型将用于确定要选择的重载。因此,Java从{}和{}中选择{},因为{}位于{}类型的变量中,而不是{}。由于使用了#1,因此输出为"AA"

    接下来的三次通话也会发生同样的情况,尽管第三次通话也很有趣:

    ab.f(bb); // AA
    

    虽然ab实际上是一个B实例,因此它有一个void f(B b)#5)打印"BB",但我们看到"AA"。这是因为ab再次位于类型为A的变量中。因此,编译器将查看A提供的方法。唯一适用的方法是f(A a)#1),因为ab肯定不是C。因此它再次使用#1并打印"AA"


    最后一个例子中的打字错误

    你的最后一个例子有点不对劲。我猜您输入错了,因为代码将输出"AC",而不是"CA"

    bb.f(cc); // AC
    

    原因很简单bbB类型,也是它的变量。因此,我们可以从B的方法中进行选择(以及从A继承的方法)。参数的类型为C,其变量的类型相同。所以我们调用方法#4,printing "AC"


    类型概述

    以下是最终相关的实际类型情况:

    A.f(A); // AA, #1
    B.f(A); // AA, #3
    A.f(B); // AA, #1
    B.f(A); // AA, #3
    B.f(C); // AC, #4
    

    JLS定义

    Java如何确定调用哪些方法的规则在Java语言规范中有明确定义。如果你感兴趣的话,你可以读它。相关章节为15.12 Method Invocation Expressions。特别是{a2}和{a3}。部分摘录:

    The class [...] is searched for all member methods that are potentially applicable to this method invocation; members inherited from superclasses and superinterfaces are included in this search.

    If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.