本文主要讲讲JAVA中的两种多态类型的一点个人见解。
首先来看Subtype polymorphism(子类型多态),即一个变量名字可以代表多个类的实例(子类型)。
之前我们所学的一般声明引用和创建对象的方法大致如下:
Dog mydog = new Dog();
//前半部分声明一个Dog类的引用变量mydog,后半部分是创建一个Dog类的对象,调用了Dog类的构造器。
重点就是引用变量的类型必须与创建对象的类型一致,譬如都为Dog。
然而在多态的情况下,引用变量和创建的对象可以是不同的类型,譬如:
Animal mydog = new Dog();
//引用变量的类型被声明为Animal,但对象是Dog
我们在运用多态时,引用类型可以是实际对象类型的父类。如何判断一个类是否是某个类的子类呢?我们这样测试:如果B是A的一个Subtype,那么每个B都是一个A。引用上面的例子,就是每个Dog都是一个Animal,即Dog类是Animal类的一个Subtype。这样运用多态时,就可以声明一个指向Dog类的Animal类的引用变量。
举个例子:
Animal []animals = new Animal[5];
animals [0] = new Dog();
animals [1] = new Cat();
animals [2] = new Wolf();
animals [3] = new Hippo();
animals [4] = new Lion();
for (int i = 0; i < animals.lenth; i++){
animals[i].eat();
}
//我们声明了一个Animal类型的数组,并且你可以放任何Animal类的子类对象进去。而下面的遍历数组中的每个元素并调用每个动物的eat方法。这就是多态很强的一个地方,你可以将数组的元素逐个调出来当作是Animal对象来操作,比如i = 0时,会调用Dog的eat,而i = 1时,会调用Cat的eat。当子类override了超类中的eat方法,那么每次调出数组中的元素时虽然都把元素当作Animal对象来操作,但实际上执行的是每个子类(不同动物)自己的eat方法。
Subtype polymorphise(子类型多态)不仅体现在构造新对象上,还能体现在方法上,即参数和返回类型也可以多态。参数可以用任何Animal类型的对象来当传入,即声明了一个Animal对象并用子类对象Dog来给它赋值也是能够作为传入参数的。
所以如果我们将程序代码写成使用多态参数,也就是将参数声明成父类型,就可以在运行的时候传入任何的子类对象。这样别人想要给我们的父类扩展一些新的子类时,我们写的使用多态参数的方法仍然可以运行。
再来看Ad hoc polymorphise(特殊多态),即当一个函数适用于几种不同的类型的时候,获得特殊多态。这里涉及到overload(重载)。重载的意义是几个方法共用一个名称,但是参数不同。你可以任意改变重载方法的返回类型,只要所有的覆盖使用不同的参数就行。但是如果只有返回类型的不同,而参数列表是一样的,这是绝对不允许的。编译器不会通过,重载的条件是要使用不同的参数,此时返回类型可以自由地定义。
重载可以有同一方法的多个不同参数版本以方便调用。因为重载方法不是用来满足定义在父类中的多态合约,所以重载的方法比较有扩展性。
既然重载是一个方法的不同实现,即方法名字是相同的,那么问题来了,调用该重载方法的时候,应该选择哪种实现来调用呢?
重载是一个静态的多态,当重载方法被调用时,编译器执行静态类型检查,通过后采用"best match technique"策略根据参数列表进行最佳匹配,在编译阶段时决定要具体执行哪个方法。举个例子:
public int add(int x, int y){
return x + y;
}
public double add(double x, double y){
return x + y;
}
以上是合法的重载方法示例。调用该重载方法时,编译器会根据你传入的参数是int类型的而自动匹配第一个方法,根据你传入的参数是double类型的会匹配第二种方法。
这就是方法的重载,重载不仅发生在当前类中,也可以在子类中重载。特别的是,当子类重载了父类的方法时,子类仍然继承来超类中被重载的方法。
以上就是个人对多态的两种类型——特殊多态和子类型多态的一点理解。