我们知道Java有一大特性就是多态。讲到多态不得不想再深层次的研究下多态中的方法分派到底是采用什么样的策略的。
还是举个例子来分析一下:
上面的代码中最终输出的应该是多少呢?
别着急,咱们先分析下上面的输出会有什么不同,我们声明的是一个父类对象,但是实例化的时候是以子类来实例化的,所以影响输出结果的第一个问题是: ① getName()方法最后输出的是"super"还是"sub",也就是调用的子类的getName还是父类的getName 第二个问题是:
② test()调用的是第一个以父类为参数的方法还是第二个以子类型为参数的方法
其实对于①问题,稍微有过编程经验的人都清楚,getName肯定是调用的是子类的方法,因为本来就是子类的实例,这也是Java多态的一种体现。 作为一个初级工程师,以上都是应该知道的。
对于第二个问涉及的是Java编译器在将test()编译成字节码的时候,需要知道参数的类型,而参数类型就是你声明变量的类型,也就是说会调用test(SuperClass)方法。所以最后会输出"a sub"。 如果你已经知道这些了,恭喜你中级工程师的水平有了。
稍微总结一下上面的内容,Java的方法分派分为两种: - 静态分派 - 方法重载分派
- 编译器就确定
- 依据调用者的声明类型和方法的参数类型匹配
- 动态分派 - 方法重写分派
发散一下
如果你做过Android开发,你一定对Groovy或多或少有一定的了解(我们在写Gradle脚本时就是使用的Groovy语言)。我们也是知道在Groovy中也是可以写Java的代码的,如果将上面的代码放到Groovy中,输出的结果一样吗?
还真不一样,输出的是"b sub",这是Groovy不同之处哈,他在被编译成字节码的时候,test()方法被编译成了CallSite.callStatic方法,而这个方法会在运行时通过反射拿到参数的实际类型,然后决定调用哪个test方法,那实际类型当然是子类了,所以会调用以子类为参数类型的方法了。
到这里,如果你能够触类旁通,不仅仅局限于一种语言,能够多语言横向对比,那么显然你具有了高工的气质了。如果你有兴趣的话,不妨对比下C++、Kotlin、Scala等语言之间有什么区别。
|