Java重载方法如何竞争

突然想起以前遇到的一道笔试题,题目大概是这样子的

// 父类
public class Father {
}

// 子类
public class Son extends Father {
}

// 调用方
public class Main {
    private static void run(Father father) {
        System.out.println("Father");
    }
    
    private static void run(Son son) {
        System.out.println("Son");
    }
    
    public static void main(String[] args) {
        Father son = new Son();
        run(son);
    }
}

问:以上代码的运行结果是什么?

我们先把这个题目放一边,现在把这个问题扩展一下

public class Main { 
    // 重载1
    private static void run(int param) {
        System.out.println("int");
    }
    
    // 重载2
    private static void run(long param) {
        System.out.println("long");
    }    
    
    // 重载3
    private static void run(Integer param) {
        System.out.println("Integer");
    }
    
    // 重载4
    private static void run(Long param) {
        System.out.println("Long");
    }
    
    // 重载5
    private static void run(float param) {
        System.out.println("float");
    }
    
    // 重载6
    private static void run(double param) {
        System.out.println("double");
    }
    
    // 重载7
    private static void run(Object param) {
        System.out.println("Object");
    }
    
    // 重载8
    private static void run(int... param) {
        System.out.println("int...");
    }
    
    public static void main(String[] args) {
        run(1);
    }
}
  • 以上的代码运行结果是什么?
  • 如果把重载1方法注释掉,运行结果是什么?
  • 如果把重载1、2、3、4这4个方法注释掉,运行结果是什么?

以上的8个重载的方法,在调用者看来,如果传入一个“范围”较小的参数,比如传入参数1,好像所有方法在单独情况下都可以调用,但是在所有方法都存在的情况下,又不知道要怎么选择。调用重载方法遵循的优先级从高到低如下:

(1)精确匹配

比如传入了参数1,默认情况1是int类型的数据,那么就会调用run(int param)方法,这个没有什么疑问。而一开始的面试题也是这种情况,变量son是一个Father类型的对象,会调用run(Father father)方法从而运行结果是Father。

(2)如果是基本类型(8种),向上转为更大范围

我们都知道一个int类型的数据和一个long类型的数据运算,会先把int类型的数据隐式转换为long类型,再进行运行,结果得到一个long类型;一个long类型和一个float类型做运算,long类型会先转换为float类型;一个float类型和一个double类型运算,float类型会先转换为double型。重载方法也是同样的道理,如果没有精确的类型,会向上找,如果把重载1方法注释掉之后,会调用run(long param),如果也没有重载2,就会调用run(float param),以此类推。

(3)通过自动拆箱和装箱

假如重载1、2、5、6都被注释了,就会找自动装箱的参数,int自动装箱是Integer,也就是会调用run(Integer param)。

(4)通过子类向上继承路线依次匹配

比如以上的例子注释掉重载1、2、3、5、6之后,调用的是run(Object param),而不是run(Long param),因为Long不是Integer的父类。

(5)通过可变参数匹配

如果1、2、3、4条规则都没法匹配到方法,就会匹配可变参数,可变参数的竞争优先级是很低的。比如以上例子把重载1、2、3、5、6、7都注释掉,就会调用run(int... param)方法。

再看另一个例子

public class Main {
    private static run(int param1, Integer param2) {}
    private static run(Integer param1, int param2) {}
    
    public static void main(String[] args) {
        run(1, 2);
    }
}

以上代码的运行结果是什么?

其实,上面的代码会编译报错,因为编译器不知道调用的时候到底是要把1自动装箱还是要把2自动装箱,调用的时候需要显式传入Integer类型的对象,而不是让编译器进行自动装箱操作,比如调用第一个方法时可以用run(1, Integer.valueOf(2)),同理,想要调用方法2时用run(Integer.valueOf(1), 2)。

说了那么多其实我们了解就行了,建议在实际的开发中尽量避免出现这样的重载方法,可以根据参数的不同改用不一样的方法名,因为这种写法其实和i++ + ++i一样会使得代码的可读性极差。

原文地址:https://www.cnblogs.com/spareyaya/p/12804079.html