Menu

Java 方法重载:同名方法,不同参数

Java 方法重载如何让多个方法共用一个名字但接收不同的参数,编译器如何选出要调用的重载版本,以及要避开的二义性陷阱。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

一个名字,多个版本

在上一页中,你已经看到方法的参数决定了它能接收什么。方法重载更进一步:只要参数列表不同,你就可以让多个方法使用同一个名字。编译器会把它们当作不同的方法,并根据你传入的实参选出合适的那个。

这就是为什么 System.out.println 能打印 int、String、boolean 或 double:println 并不只有一个,而是有很多个共用这个名字的重载版本。你按自己的意思写出调用,编译器就会把它匹配到合适的版本。

两个方法都叫 square,但一个接收 int,另一个接收 double。字面量 5 是 int,所以运行第一个重载;2.5 是 double,所以运行第二个。

怎样才算不同的重载

重载版本必须在参数列表上有所不同,也就是至少满足以下一条:

  • 参数的个数不同,
  • 参数的类型不同,或者
  • 类型的顺序不同。

每个调用的参数列表都只与三个 join 方法中的一个完全匹配,所以不会产生混淆。

返回类型不算数

一个常见的初学者陷阱:试图只靠返回类型来重载。返回类型不属于编译器使用的签名,所以下面的代码无法通过编译:

// Does NOT compile - same name, same parameters, only the return type differs
static int   value() { return 1; }
static double value() { return 1.0; }   // error: value() is already defined

编译器分不清这两个方法,因为在 value() 这样的调用处,实参中没有任何线索表明你想要哪一个。重载版本可以有不同的返回类型,但前提是它们的参数列表本来就不同。

Java 如何选择重载版本

当有多个重载版本都能接收你的实参时,Java 会选择最具体的那个,并且优先选择类型完全匹配的,而不是需要拓宽转换的。看看传入 int 实参时会发生什么:

show(7) 与 int 完全匹配,尽管 long 和 double 经过拓宽后也能容纳 7。只有当完全匹配的重载被删除时,编译器才会把 int 拓宽成 long,再拓宽成 double。这种解析完全在编译时完成,依据的是实参的声明类型。

当心有二义性的调用

如果没有哪一个重载明显是最佳匹配,编译器就不会去猜,而是报错。这种情况最常出现在 null 上,因为它适用于任何引用类型:

static void handle(String s) { }
static void handle(StringBuilder b) { }

handle(null);   // error: reference to handle is ambiguous

两个重载都接收 null,而且哪个都不比另一个更具体,所以这个调用无法通过编译。解决办法是用强制转换明确指定类型,写成 handle((String) null),或者重新设计,让重载版本不再冲突。混用自动装箱和拓宽转换时也要同样小心;让重载集合保持简单,使每个调用都只有一个明显的胜出者。

重载构造方法

重载不只用于普通方法,构造方法也经常用它来提供多种创建对象的方式。无参构造方法可以用 this(...) 把工作交给参数更完整的构造方法:

这两个构造方法都叫 Point,但参数个数不同,和重载方法完全一样。用 this(...) 委托可以把初始化逻辑集中在一个地方。

重载与重写

这两个词听起来很像,但毫无关系:

  • 重载:名字相同,参数列表不同,位于同一个类中。编译器在编译时选择版本。它的目的是为一个操作提供多种变体。
  • 重写:子类用相同的名字和相同的参数重新定义继承来的方法。Java 在运行时根据对象的实际类型选择版本。它的目的是替换行为(你会在继承和多态中遇到它)。

如果参数列表完全相同,那就是重写(或者在同一个类中造成方法重复定义的错误);如果参数列表不同,那就是重载。

下一页:可变参数

重载让你可以把 join(a, b) 和 join(a, b, c) 写成不同的方法。但如果你想接收任意个数的实参,又不想为每种个数都声明一个重载呢?Java 的可变参数(varargs)语法能让一个方法接收长度可变的参数列表,这就是下一页的内容。

常见问题

Java 中的方法重载是什么?

方法重载是指在同一个类中定义多个同名方法,每个方法的参数列表都不同(参数个数不同、类型不同,或者类型的顺序不同)。编译器把你传入的实参与每个重载版本的参数逐一比对,来决定调用哪个版本。这是在编译时做出的决定,而不是运行时。

Java 中两个方法能只在返回类型上不同吗?

不能。对重载来说,返回类型不属于方法签名,所以同一个类中同时有 int total() 和 double total() 会导致编译错误。重载版本必须在参数列表上有所不同:参数的个数、类型或顺序。返回类型可以不同,但前提是参数已经不同,单靠返回类型不同是不行的。

Java 中重载和重写有什么区别?

重载是在同一个类中有多个同名但参数不同的方法,由编译器在编译时决定调用哪个。重写是子类用同名且同参数的方法重新定义继承来的方法,在运行时根据对象的实际类型决定调用哪个。重载是为了提供多种变体,重写是为了替换行为。

Coddy 编程语言插图

和 Coddy 一起学编程

开始