为什么需要方法重载?
在编程语言中,名字的使用很重要。创建对象的时候,我们给一块内存区域起一个名字,然后这个名字就是我们创建的对象的引用,只要我们“叫”这个名字,计算机就知道我们在对哪一块内存区域进行操作。同理,方法也有名字,只是它的名字不是代表一块存储区域,而是代表一个动作。
在人类的语言中,我们会说:洗衣、洗碗、洗车,对于同一个动作“洗”,我们并没有因为后面接的具体名词的不同赋予这个动作不同的名字,比如“衣洗”衣,“碗洗”碗,“车洗”车。因为这是一种冗余,同一个动作“洗”,我们完全可以根据上下文来判定要洗的是什么。对于编程语言,我们可能调用print()方法来输出整型变量、输出浮点型变量、输出字符串等等,在c语言中针对不同的输出类型需要提供不同的函数名,而在Java中,我们则直接使用通用的print()方法即可。
而另一个促使方法重载的原因就是构造函数,构造函数名必须与类名一致,但是如果我们需要多种对象的构造方法呢?这时候方法重载就是必须的了。
Java如何区分重载的方法?
对于只有一个方法使用方法名,在调用它的时候,我们自然知道该调用哪个方法;但是如果有多个方法共用一个名字,如何区分要调用的是哪一个方法?Java的处理方式是,每个重载的方法都应该具有独一无二的参数列表,独一无二的参数列表包括参数的个数不同;参数的类型不同;参数的顺序不同。
值得注意的是只有返回值类型不同,无法区分方法,例如有两个方法:
int f() { return 1; }
void f() { }
这两个方法的签名是一样的(方法名+参数列表),只有返回值不同。当我们调用int x = f();
时,我们知道肯定会调用返回值为int的方法int f()
,但是当我们调用f();
时,就不知道该调用哪一个方法了,因为Java允许有返回值的方法在调用时忽略其返回值。
基本数据类型的重载
由于基本数据类型都能自动向上转型,结合重载来看,可能比较难以理解。以下通过举例来说明。
public class PrimitiveOverloading {
private void f1(char x) { System.out.print("f1(char) "); }
private void f1(byte x) { System.out.print("f1(byte) "); }
private void f1(short x) { System.out.print("f1(short) "); }
private void f1(int x) { System.out.print("f1(int) "); }
private void f1(long x) { System.out.print("f1(long) "); }
private void f1(float x) { System.out.print("f1(float) "); }
private void f1(double x) { System.out.print("f1(double) "); }
private void f2(byte x) { System.out.print("f2(byte) "); }
private void f2(short x) { System.out.print("f2(short) "); }
private void f2(int x) { System.out.print("f2(int) "); }
private void f2(long x) { System.out.print("f2(long) "); }
private void f2(float x) { System.out.print("f2(float) "); }
private void f2(double x) { System.out.print("f2(double) "); }
private void f3(short x) { System.out.print("f3(short) "); }
private void f3(int x) { System.out.print("f3(int) "); }
private void f3(long x) { System.out.print("f3(long) "); }
private void f3(float x) { System.out.print("f3(float) "); }
private void f3(double x) { System.out.print("f3(double) "); }
private void f4(int x) { System.out.print("f4(int) "); }
private void f4(long x) { System.out.print("f4(long) "); }
private void f4(float x) { System.out.print("f4(float) "); }
private void f4(double x) { System.out.print("f4(double) "); }
private void f5(long x) { System.out.print("f5(long) "); }
private void f5(float x) { System.out.print("f5(float) "); }
private void f5(double x) { System.out.print("f5(double) "); }
private void f6(float x) { System.out.print("f6(float) "); }
private void f6(double x) { System.out.print("f6(double) "); }
private void f7(double x) { System.out.print("f7(double) "); }
private void testConstVal() {
System.out.print("5: ");
f1(5); f2(5); f3(5); f4(5); f5(5); f6(5); f7(5);
System.out.println();
}
private void testChar() {
char x = 'x';
System.out.print("char: ");
f1(x); f2(x); f3(x); f4(x); f5(x); f6(x); f7(x);
System.out.println();
}
private void testByte() {
byte x =0;
System.out.print("byte: ");
f1(x); f2(x); f3(x); f4(x); f5(x); f6(x); f7(x);
System.out.println();
}
private void testShort() {
short x =0;
System.out.print("short: ");
f1(x); f2(x); f3(x); f4(x); f5(x); f6(x); f7(x);
System.out.println();
}
private void testInt() {
int x =0;
System.out.print("int: ");
f1(x); f2(x); f3(x); f4(x); f5(x); f6(x); f7(x);
System.out.println();
}
private void testLong() {
long x =0;
System.out.print("long: ");
f1(x); f2(x); f3(x); f4(x); f5(x); f6(x); f7(x);
System.out.println();
}
private void testFloat() {
float x =0;
System.out.print("float: ");
f1(x); f2(x); f3(x); f4(x); f5(x); f6(x); f7(x);
System.out.println();
}
private void testDouble() {
double x =0;
System.out.print("double: ");
f1(x); f2(x); f3(x); f4(x); f5(x); f6(x); f7(x);
System.out.println();
}
public static void main(String[] args) {
PrimitiveOverloading p = new PrimitiveOverloading();
p.testConstVal();
p.testChar();
p.testByte();
p.testShort();
p.testInt();
p.testLong();
p.testFloat();
p.testDouble();
}
}
result:
5: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
char: f1(char) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
byte: f1(byte) f2(byte) f3(short) f4(int) f5(long) f6(float) f7(double)
short: f1(short) f2(short) f3(short) f4(int) f5(long) f6(float) f7(double)
int: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
long: f1(long) f2(long) f3(long) f4(long) f5(long) f6(float) f7(double)
float: f1(float) f2(float) f3(float) f4(float) f5(float) f6(float) f7(double)
double: f1(double) f2(double) f3(double) f4(double) f5(double) f6(double) f7(double)
从上述结果来看,常量5会被当做int,如果重载的方法中含有参数类型是int的,就会调用该方法;如果某个变量的数据类型小于重载方法中所有的数据类型,则该变量会向上转型;char类型比较特殊,如果参数类型不含char,则char会向上转型为int,然后按照int类型的规则来转型。
如果某个变量的数据类型大于重载方法中所有数据类型呢?我们知道向上转型是会自动发生的,但是向下转型却不会,它需要进行强制类型转换。如果不进行转换直接调用,则无法通过编译。
以下是向下转型时的正确使用方法,在上述实例中添加方法:
private void testWideDouble() {
double x = 0;
System.out.print("double argument: ");
f1((char) x); f1((byte) x); f1((short) x); f1((int) x); f1((long) x); f1((float) x); f1(x);
System.out.println();
}
在main函数中调用它
p.testWideDouble();
result:
double argument: f1(char) f1(byte) f1(short) f1(int) f1(long) f1(float) f1(double)