避免带有变长参数的方法重载

时间:2022-08-27 22:11:43
       在项目和系统的开发中, 为了提高方法的灵活度和可复用性, 我们经常要传递不确定数量的参数到方法中, 在 Java 5 之前常用的设计技巧就是把形参定义成 Collection 类型或其子类类型, 或者是数组类型, 这种方法的缺点就是需要对空参数进行判断和筛选, 比如实参为null 值和长度为 0 的 Collection 或数组。 而 Java 5 引入变长参数( varags) 就是为了更好地提高方法的复用性, 让方法的调用者可以“随心所欲”地传递实参数量, 当然变长参数也是要遵循一定规则的, 比如变长参数必须是方法中的最后一个参数 ; 一个方法不能定义多个变长参数等, 这些基本规则需要牢记, 但是即使记住了这些规则, 仍然有可能出现错误, 我们来看如下代码:
   
   
  1. public class Client {
  2. public void add(int a,int b) {
  3. float c=a+b+b;
  4. System.out.println("c="+c);
  5. }
  6. public void add(int a,int... b) {
  7. float c=a;
  8. for (int i : b) {
  9. c=c+i;
  10. }
  11. System.out.println("c="+c);
  12. }
  13. public static void main(String[] args) {
  14. Client client=new Client();
  15. client.add(1,2);
  16. }
  17. }
     我们来仔细看看这两个方法, 它们是重载吗? 当然是了, 重载的定义是“方法名相同, 参数类型或数量不同” , 很明显这两个方法是重载。 但是再仔细瞧瞧, 这个重载有点特殊: add(int a,int... b) 的参数范畴覆盖了add(int a,int b)的参数范畴。 那问题就出来了 : 对于 add(int a,int b)这样的计算, 到底该调用哪个方法来处理呢?
     我们知道 Java 编译器是很聪明的, 它在编译时会根据方法签名( Method Signature) 来确定调用哪个方法, 比如 add(1,2,3)这个调用, 很明显 2和 3会被转成一个包含两个元素的数组, 并传递到 add(int a,int... b)中, 因为只有这一个方法签名符合该实参类型, 这很容易理解。 但是我们现在面对的是 add(1,2)调用, 这个“2”既可以被编译成 int 类型的“2” , 也可以被编译成 int 数组“{2}” , 即只包含一个元素的数组。 那到底该调用哪一个方法呢?       我们先运行一下看看结果, 运行结果是:                                                                        c=5.0

      看来是调用了第一个方法, 为什么会调用第一个方法, 而不是第二个变长参数方法呢?因为 Java 在编译时, 首先会根据实参的数量和类型(这里是 2 个实参, 都为 int 类型, 注意没有转成 int 数组) 来进行处理, 也就是查找到add(int a,int b)方法, 而且确认它是否符合方法签名条件。 现在的问题是编译器为什么会首先根据 2 个 int 类型的实参而不是 1 个 int 类型、 1 个 int 数组类型的实参来查找方法呢? 这是个好问题, 也非常好回答 :        因为 int 是一个原生数据类型, 而数组本身是一个对象, 编译器想要“偷懒” , 于是它会从最简单的开始“猜想” , 只要符合编译条件的即可通过, 于是就出现了此问题。         问题是阐述清楚了, 为了让我们的程序更易读,还是慎重考虑变长参数的方法重载吧, 否则让人伤脑筋不说, 说不定哪天就陷入这类小陷阱里了。