为什么要慎用重载?
很多公司开发规范不推荐员工使用重载,甚至你会发现一些框架不支持重载(比如MyBatis接口方法不支持重载)。那么重载到底有什么问题,让很多人如临大敌?我们先通过代码示例来看看输出,这样更直观一些:
![acbf0842cd8392b68ba1bb4add8abb91.png](https://i-blog.csdnimg.cn/blog_migrate/67d816ec925576a7eca0ca4141e5b34c.jpeg)
重载代码
代码很简单,Cat和Dog类继承自Animal类。同时在Comm类中针对三个类定义了三个重载方法。然后在主方法里有两组调用:一组是直接调用,一组是放入Animal数组中通过for-each循环调用。两组的输出结果一样吗?
![e522c79e15c4f10a9a92fe502cbbc0b8.png](https://i-blog.csdnimg.cn/blog_migrate/53e7cdb3818722ecff6f68ec184c6ff4.jpeg)
重载的输出结果
可以看到,第一组调用输出结果和我们预期一致,第二组调用全部输出“this is other animals”。
先说结论,原因下面再分析:Java对重载方法的选择是在静态编译期确定的,对重写方法的选择是在运行期确定的。(搞清楚重载和重写的区别:方法名相同,参数也一致是重写;方法名相同,参数不同是重载)
因为对重载方法的选择是在静态编译期确定的,所以上面代码中,Java对for-each循环语句进行编译时,并不清楚也不关注 Animal[] 数组中存放的元素的具体类型,它将数组元素全部当作Animal对象编译。
这里给一个证明“对重写方法的选择在运行期确定”的例子,虽然和标题无关,但对比记忆更深刻:
![a6f006593901e2bcf207839bbec50ed8.png](https://i-blog.csdnimg.cn/blog_migrate/7f85a7cd9dbfd4ea72c3bf7764b5ce0a.jpeg)
重写代码
重写的代码也很简单:Animal中定义了一个printName方法,其子类Cat和Dog重写了这个方法。在主方法中同样有两组调用:一组直接调用,一组放入Animal数组中通过for-each循环调用。我们来看看结果:
![d950df096f752b263ae0c21dde803ddc.png](https://i-blog.csdnimg.cn/blog_migrate/f2fcf9daa06841b09cd36fe2daeb7749.jpeg)
重写代码的输出结果
可以看到,重写和重载的执行结果并不相同,重写的两组的输出结果相同,都符合预期。
上面重载的例子,我们可以这样解决:
不使用三个重载方法,而是直接使用一个classify方法就可以了,代码逻辑也很简单:
![b2bafa1ab58aa00518ac6ff300c95b1c.png](https://i-blog.csdnimg.cn/blog_migrate/19487431c7bf9e91e880629f96c6ca22.jpeg)
新classify方法
会在哪里遇到问题
说到重载,我们在什么时候使用的最多?肯定是构造器重载吧。一个类的多个构造器肯定是重载的。所以我们以后为一个类编写多个构造器时,一定要考虑到这个问题。
虽然很多规范要求尽量不要使用重载,但像构造器这样不得不使用重载时应该怎么办?这时候应该尽量避免参数只需要经过类型转换就可以被传递给不同的重载方法。
看到“参数只需要经过类型转换就可以被传递给不同的方法”,这句话有没有让你想到被Java的自动装箱和自动拆箱支配的恐惧?我之前准备面试的时候,遇到一道题:很多重载方法,区别就在与参数分别是不同的数据类型,然后通过各种输入让你判断到底会调用哪个重载方法,输出什么样的结果。所以如果你的重载方法参数涉及到Java装箱和拆箱,需要好好理一理才行。
Java装箱和拆箱问题后面会专门写一篇文章。最后只需要记住:“能重载”不意味着“要重载”。更多时候我们应该尽量避免重载方法。