从类型体系的角度理解继承和多态
- 继承是一个类的子类,自动具备了父类的属性和方法,除非被父类声明为私有的
多态是同一个类的不同子类,在调用同一个方法时会执行不同的动作 ```javascript // 面向对象继承和多态示例 class Mammal{ int weight = 20;
boolean canSpeak(){return true;
}
void speak(){
println("mammal speaking...");
} }
class Cow extends Mammal{ void speak(){ println(“moo~~ moo~~”); } }
class Sheep extends Mammal{ void speak(){ println(“mee~~ mee~~”); println(“My weight is: “ + weight); //weight的作用域覆盖子类 } }
//将子类的实例赋给父类的变量 Mammal a = Cow(); Mammal b = Sheep();
//canSpeak()方法是继承的 println(“a.canSpeak() : “ + a.canSpeak()); println(“b.canSpeak() : “ + b.canSpeak());
//下面两个的叫声会不同,在运行期动态绑定方法 a.speak(); //打印牛叫 b.speak(); //打印羊叫
<a name="mo8Ru"></a>### 子类型(subtype)面向对象编程时,我们可以给某个类创建不同的子类,实现一些个性化的功能;写程序时,我们可以站在抽象度更高的层次上,不去管具体的差异。子类型的核心是提供了 is-a 的操作。也就是对某个类型所做的所有操作都可以用子类型替代。它可以放宽对类型的检查,从而导致多态。<a name="V0xwe"></a>### 子类型两种实现方式- 名义子类型(Nominal Subtyping):`Java` 和 `C++` 语言- 显式声明继承了什么类,或者实现了什么接口- 结构化子类型(Structural Subtyping)- 一个类不需要显式地说自己是什么类型,只要它实现了某个类型的所有方法<a name="RGYJS"></a>## 语义分析1. 从类型处理的角度出发,我们要识别出新的类型:`Mammal`、`Cow` 和 `Sheep`1. 设置正确的作用域1. 对变量和函数做类型的引用消解用 `Mammal` 来声明这两个变量 a,b。按照类型推导的算法,a 和 b 都是 `Mammal`,这是个 I 属性计算的过程。也就是说,在编译期,我们无法知道变量被赋值的对象确切是哪个子类型,只知道声明变量时,它们是哺乳动物类型,至于是牛还是羊,就不清楚了。<br />正确的消解,是要指向 Cow 和 Sheep 的 speak 方法,只能到运行期再解决。<a name="UTPAW"></a>## 在运行期实现方法的动态绑定在运行期,我们能知道 a 和 b 这两个变量具体指向的是哪个对象,对象里是保存了真实类型信息的。ClassObject 的 type 属性会指向一个正确的 Class,这个类型信息是在创建对象的时候被正确赋值的。<br /><a name="i79eI"></a>### 实现多态在调用类的属性和方法时,我们可以根据运行时获得的,确定的类型信息进行动态绑定。下面这段代码是从本级开始,逐级查找某个方法的实现,如果本级和父类都有这个方法,那么本级的就会覆盖掉父类的。```javascriptprotected Function getFunction(String name, List<Type> paramTypes){//在本级查找这个这个方法Function rtn = super.getFunction(name, paramTypes);//如果在本级找不到,那么递归的从父类中查找if (rtn == null && parentClass != null){rtn = parentClass.getFunction(name,paramTypes);}return rtn;}
获取类型信息,这种机制就叫做运行时类型信息(Run Time Type Information, RTTI)。C++、Java 等都有这种机制,比如 Java 的 instanceof 操作,就能检测某个对象是不是某个类或者其子类的实例。
继承情况下对象的实例化
不仅要初始化自己这一级的属性变量,还要把各级父类的属性变量也都初始化。
//从父类到子类层层执行缺省的初始化方法,即不带参数的初始化方法protected ClassObject createAndInitClassObject(Class theClass) {ClassObject obj = new ClassObject();obj.type = theClass;Stack<Class> ancestorChain = new Stack<Class>();// 从上到下执行缺省的初始化方法ancestorChain.push(theClass);while (theClass.getParentClass() != null) {ancestorChain.push(theClass.getParentClass());theClass = theClass.getParentClass();}// 执行缺省的初始化方法StackFrame frame = new StackFrame(obj);pushStack(frame);while (ancestorChain.size() > 0) {Class c = ancestorChain.pop();defaultObjectInit(c, obj);}popStack();return obj;}
this 和 super
public class ThisSuperTest {public static void main(String args[]){//创建Cow对象的时候,会在Mammal的构造方法里调用this.reportWeight(),这里会显示什么Cow cow = new Cow();System.out.println();//这里调用,会显示什么cow.speak();}}class Mammal{int weight;Mammal(){System.out.println("Mammal() called");this.weight = 100;}Mammal(int weight){this(); //调用自己的另一个构造函数System.out.println("Mammal(int weight) called");this.weight = weight;//这里访问属性,是自己的weightSystem.out.println("this.weight in Mammal : " + this.weight);//这里的speak()调用的是谁,会显示什么数值this.speak();}void speak(){System.out.println("Mammal's weight is : " + this.weight);}}class Cow extends Mammal{int weight = 300;Cow(){super(200); //调用父类的构造函数}void speak(){System.out.println("Cow's weight is : " + this.weight);System.out.println("super.weight is : " + super.weight);}}// resultMammal() calledMammal(int weight) calledthis.weight in Mammal : 200Cow's weight is : 0super.weight is : 200Cow's weight is : 300super.weight is : 200
