特殊多态(Ad hoc polymorphism)
一个特殊多态的例子
定义:一个方法可以有多个同名的实现,即方法重载。
当一个函数适用于多种不同的类型时,就会出现特殊多态,例如:
public class OverloadExample{
public static void main(String[] args){
System.out.println(2,3);
System.out.println("A","B");
System.out.println("C","D","E");
}
public static String add(String a, String b){
return a.concat(b);
}
public static String add(String a, String b, String c){
return a.concat(b).concat(c);
}
public static int add(int a, int b){
return a + b;
}
}
重载使得多种不同的方法有了同样的名字,但有不同的参数列表或者返回值类型。
作用:方便客户端调用,客户端可以用不同的参数列表调用同样的函数。
重载方法属于静态多态,它会根据参数列表进行静态匹配,在调用位置进行静态类型检查,在编译时具体决定执行什么方法(与之相反,方法重写需要在运行时进行动态检查)。
方法重载的规则
- 可以定义相同/不同的参数列表。
- 可以定义相同/不同的返回值类型。
- 可以定义相同/不同的访问权限。
- 可以定义相同/不同的异常。
- 一个方法可以在同一个类中重载,也可以在子类中重载。
方法重载的进一步讨论
我们定义若干动物类如下:
class Animal{
public void eat(){}
}
class Horse extends Animal{
public void eat(String food){}
}
public class UseAnimals{
public void doStuff(Animal a){
System.out.println("Animal");
}
public void doStuff(Horse a){
System.out.println("Horse");
}
}
注意:这里子类和父类的eat方法之间是重载的关系,因为它们的方法签名不同。
我们定义测试类如下:
public class TestUseAnimals{
public static void main(String[] args){
UseAnimals ua = new UseAnimals();
Animal animalobj = new Animal();
Horse horseobj = new Horse();
Animal animalRefToHorse = new Horse();
ua.doStuff(animalobj);
ua.doStuff(horseobj);
ua.doStuff(animalRefToHorse);
}
}
运行结果如下:
特别注意第三个,方法重载匹配方法签名时按照其声明类型为准。
我们添加对eat的具体实现:
class Animal{
public void eat(){
System.out.println("Animal eat nothing");
}
}
class Horse extends Animal{
public void eat(String food){
System.out.println("Horse eat"+food);
}
public void eat(){
System.out.println("Horse eat grass");
}
}
public class UseAnimals{
public void doStuff(Animal a){
System.out.println("Animal");
}
public void doStuff(Horse a){
System.out.println("Horse");
}
}
改写测试代码:
public class TestUseAnimals{
public static void main(String[] args){
Animal animalobj = new Animal();
Horse horseobj = new Horse();
Animal animalRefToHorse = new Horse();
animalobj.eat();//1
horseobj.eat();//2
animalRefToHorse.eat();//3
animalobj.eat("APPLE");//4
horseobj.eat("BANANA");//5
animalRefToHorse.eat("BANANA");//6
}
}
该代码不能运行,因而我们将逐一分析各个语句的执行。
语句1将直接调用Animal类中的eat方法,输出Animal eat nothing。
语句2使用了方法重写,将会使用Horse类中重写的eat方法。
语句3将调用Horse类中重写的方法,在静态检查的过程中,程序会检查Animal中是否有eat方法,而在运行时,程序会选择执行Horse中的eat方法。
语句4将会报错,因为静态检查时发现animal中不存在eat(String xxx)的方法。
语句5将会输出Horse eat BANANA
语句6将报编译时错误,因为静态检查时发现animal中不存在eat(String xxx)的方法,注意与语句3进行比较。
重载与重写的区别
- 重写时子类与父类具有相同的签名。
- 签名不同时为重载。
- 子类重载了父类的方法后,子类仍然继承了父类的方法。