向上转型:子类对象转为父类,父类可以是接口。公式:Father f = new Son();Father是父类或接口,son是子类。
向下转型:父类对象转为子类。公式:Son s = (Son)f;
我们将形参设为父类Animal类型,当执行test.f(c)时,内存情况如下图:
c作为Cat类型传入,Animal a作为形参,相当于执行了Animal a = new Cat(),这时a和c同时指向Cat对象,但此时a不能访问Cat类扩展的数据成员,所以再将a强转成Cat类型即可。如果不存在这样的转型机制,则针对猫和狗我们还要分别写两个函数f(Cat c)和f(Dog d)。但其实上图程序的可扩展性也不是最好的。我们还可以利用动态绑定(即多态)将扩展性进一步提升。多态机制的三个前提分别是:
(1)要有继承
(2)要重写,即子类对父类中某些方法进行重新定义
(3)要向上转型,用父类引用指向子类对象。
下面来看一个例子:
class Animal { private String name;
Animal(String name) { this.name = name;
}
public void enjoy() {
System.out.println("动物的叫声……");
}
}class Cat extends Animal { private String eyesColor;
Cat(String n, String c) {
super(n);
eyesColor = c;
}
public void enjoy() {
System.out.println("我养的猫高兴地叫了一声……");
}
}class Dog extends Animal {
private String furColor;
Dog(String n, String c) {
super(n);
furColor = c;
}
public void enjoy() {
System.out.println("我养的狗高兴地叫了一声……");
}
}class Bird extends Animal {
Bird() {
super("bird");
}
public void enjoy() {
System.out.println("我养的鸟高兴地叫了一声……");
}
}class Lady {
private String name; private Animal pet;
Lady(String name, Animal pet) { this.name = name; this.pet = pet;
}
public void myPetEnjoy() {
pet.enjoy();
}
}public class Jerque { public static void main(String args[]) {
Cat c = new Cat("Catname", "blue");
Dog d = new Dog("Dogname", "black");
Bird b = new Bird();
Lady l1 = new Lady("l1", c);
Lady l2 = new Lady("l2", d);
Lady l3 = new Lady("l3", b);
l1.myPetEnjoy();
l2.myPetEnjoy();
l3.myPetEnjoy();
}
}
上面的例子中,我们发现,如果我们想要加入新的动物,只需定义相应的类继承Animal,完全不用动任何一处代码,因为这里运用了面向对象最核心的东西——多态。与之前的例子不同,虽然我们一直强调当用父类的引用指向子类对象,父类无法访问子类自己的成员,但是方法与数据成员不同,具体调哪一个方法是等到运行时决定的,new出了什么对象就调用相应对象的方法,取决于实际new出的对象而不是指向对象的引用,所以当传入不同动物类型,mypetEnjoy()就会去执行不同的方法



