下面代码输出是什么:
class A { public String run(D obj) { return ("A & D"); } public String run(A obj) { return ("A & A"); } } class B extends A { public String run(B obj) { return ("B & B"); } public String run(A obj) { return ("B & A"); } } class C extends B {} class D extends B {} public class Test { public static void main(String[] args) { A aa = new A(); A ab = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println("1--" + aa.run(b)); System.out.println("2--" + aa.run(c)); System.out.println("3--" + aa.run(d)); System.out.println("4--" + ab.run(b)); System.out.println("5--" + ab.run(c)); System.out.println("6--" + ab.run(d)); System.out.println("7--" + b.run(b)); System.out.println("8--" + b.run(c)); System.out.println("9--" + b.run(d)); } }关于上面所有注释答案的解释其实就一个核心秘笈,多态是对象在不同时刻表现出来的多种状态,是一种编译时期状态和运行时期状态不一致的现象。 在编写或者分析代码时记住如下口诀:
-
成员变量:编译看左,运行看左(因为无法重写);
-
成员方法:编译看左,运行看右(因为普通成员方法可以重写,变量不可以);
-
静态方法:编译看左,运行看左(因为属于类);
1中 aa 在编译时取决于左边 A 的类型,所以包含了参数为 A、D 的 run 方法,而运行时传递给 run 方法的参数类型为 A,所以执行了 A 类中参数为 A 类型的 run 方法。
2中 aa 在编译时取决于左边 A 的类型,所以包含了参数为 A、D 的 run 方法,而运行时传递给 run 方法的参数类型为 C,而此时只支持参数为 A、D 的 run 方法,而 C 又继承自 B,B 继承自 A,所以执行了 A 类中参数为 A 类型的 run 方法。
3中 aa 在编译时取决于左边 A 的类型,所以包含了参数为 A、D 的 run 方法,而运行时传递给 run 方法的参数类型为 D,而此时恰巧支持参数为 A、D 的 run 方法,所以直接执行了 A 类中参数为 D 类型的 run 方法。
4中 ab 在编译时取决于左边 A 的类型,运行时为右边 B 的类型,所以编译时包含了参数为 A、D 的 run 方法,而运行时传递给 run 方法的参数类型为 B,所以对应的方法为 A 类中参数为 A 类型的 run 方法,而由于 ab 在运行时右侧的 B 类中重写了 A 类中参数为 A 类型的 run 方法,所以运行时最终执行了 B 类中重写的参数为 A 类型的 run 方法(所以类 B 中参数为 B 的 run 方法其实是 B 类特有的重载方法,而不是重写方法)。
5中 ab 在编译时取决于左边 A 的类型,运行时为右边 B 的类型,所以编译时包含了参数为 A、D 的 run 方法,而运行时传递给 run 方法的参数类型为 C,C 又最终继承自 A,所以对应的方法为 A 类中参数为 A 类型的 run 方法,而由于 ab 在运行时右侧的 B 类中重写了 A 类中参数为 A 类型的 run 方法,所以运行时最终执行了 B 类中重写的参数为 A 类型的 run 方法(所以类 B 中参数为 B 的 run 方法其实是 B 类特有的重载方法,而不是重写方法)。
6中 ab 在编译时取决于左边 A 的类型,运行时为右边 B 的类型,所以编译时包含了参数为 A、D 的 run 方法,而运行时传递给 run 方法的参数类型为 D,所以对应的方法为 A 类中参数为 D 类型的 run 方法(所以类 B 中参数为 B 的 run 方法其实是 B 类特有的重载方法,而不是重写方法)。
7中 b 在编译时取决于左边 B 的类型,运行时为右边 B 的类型,所以编译时包含了参数为 A、B、D 的 run 方法,而运行时传递给 run 方法的参数类型为 B,所以对应的方法为 B 类中参数为 B 类型的 run 方法(B 在编译时已经继承了 A 的方法)。
8中 b 在编译时取决于左边 B 的类型,运行时为右边 B 的类型,所以编译时包含了参数为 A、B、D 的 run 方法,而运行时传递给 run 方法的参数类型为 C,而 C 的第一父类是 B,此时恰巧 B 中有支持参数为 B 的 run 方法(所以不用再往上找),所以对应的方法为 B 类中参数为 B 类型的 run 方法。
9中 b 在编译时取决于左边 B 的类型,运行时为右边 B 的类型,所以编译时包含了参数为 A、B、D 的 run 方法,而运行时传递给 run 方法的参数类型为 D,所以对应的方法为 B 类中从 A 类继承来的参数为 D 类型的 run 方法。