编译器优先选择最精确匹配的方法,varargs仅在前三阶段无匹配时启用;多态(向上转型)属于第二阶段,优先于第三阶段的varargs。

Java中方法重载的匹配规则里,变长参数(varargs)和多态(即参数类型向上转型)的优先级是有明确顺序的:编译器优先选择最精确匹配的方法,变长参数是最后才考虑的备选方案,而多态(如子类传给父类参数)属于“宽泛匹配”,但仍在varargs之前。
重载解析分三阶段,varargs排在最后
Java编译器按严格顺序尝试匹配:
- 第一阶段:只考虑不涉及自动装箱/拆箱、不涉及向上转型、也不用varargs的方法——即完全精确匹配(参数类型、数量、顺序全等)。
- 第二阶段:允许基本类型自动转换(如int→long)、引用类型向上转型(如String→Object),但仍排除varargs方法。
- 第三阶段:才启用varargs,并允许结合自动转型(比如传入数组或单个元素后自动包装成数组)。
这意味着,哪怕有一个接受Object...的varargs方法,只要存在一个接受Object的非varargs方法,编译器就一定选后者。
多态参数 vs varargs:谁更“近”就选谁
当多个重载方法都处于同一阶段(比如都在第二阶段),编译器会选“更具体的”那个。例如:
立即学习“Java免费学习笔记(深入)”;
class A {}class B extends A {}
void m(A a) { }
void m(B b) { }
void m(A... a) { }
调用 m(new B()) 时,m(B b) 比 m(A a) 更具体,所以选前者;m(A...) 属于第三阶段,直接被跳过。
常见陷阱:varargs与自动转型同时出现
如果某个调用既可走转型又可走varargs,编译器仍坚持阶段优先:
-
void f(int i)和void f(int... i)同时存在,调用f(5)—— 选前者(第一阶段精确匹配)。 -
void f(Number n)和void f(Object... os)同时存在,传f(new Integer(1))—— 选前者(第二阶段,Integer→Number 是合法向上转型;而Object...属于第三阶段)。 - 但如果只有
f(Object...)和f(Object o),传null会产生歧义(两者都匹配第二阶段),编译失败。
如何避免模糊和意外行为
- 尽量避免在同一组重载中混用varargs和普通参数,尤其不要让varargs方法和“能通过向上转型匹配”的方法并存。
- 必要时用显式转型或构造数组来控制调用目标,例如:
f(new Object[]{x})强制走varargs分支。 - 使用IDE的“Go to Declaration”或编译错误提示,观察实际解析结果,比凭直觉判断更可靠。


















