What & How & Why

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录前一修订版
后一修订版
前一修订版
cs:programming:java:courses:gtx_cs1311x:oop_n_algorithms [2024/01/22 11:10] – [Polymorphism] codingharecs:programming:java:courses:gtx_cs1311x:oop_n_algorithms [2024/01/22 12:36] (当前版本) – [实例:扔骰子] codinghare
行 250: 行 250:
     - 访问私有变量一定要通过 Accessor     - 访问私有变量一定要通过 Accessor
     - 除了 ''Math.Random()'',Java 还提供了 ''util.Random'' 供使用。本例使用了 ''Random.nexInt(bound)'' 实现了骰子的随机结果。     - 除了 ''Math.Random()'',Java 还提供了 ''util.Random'' 供使用。本例使用了 ''Random.nexInt(bound)'' 实现了骰子的随机结果。
 +      - ''Random.nexInt(bound)'' 返回的区域是 $[0, bound)$
 //Source code with comments//: {{ :cs:programming:java:courses:gtx_cs1311x:craps.zip |}} //Source code with comments//: {{ :cs:programming:java:courses:gtx_cs1311x:craps.zip |}}
 ====Inheritance==== ====Inheritance====
行 933: 行 934:
 ====Polymorphism==== ====Polymorphism====
 **多态**(//Polymorphism//)允许我们使用一段代码灵活吃处理不同类型的对象。 **多态**(//Polymorphism//)允许我们使用一段代码灵活吃处理不同类型的对象。
-==Java 处理多态的过程==+===Java 处理多态的过程==
 +<code js> 
 +Canine pixy; 
 +pixy = new Poodle(..); 
 +pixy.bark(); 
 +</code>
   - Java 会首先查找声明中的类型(declar type)   - Java 会首先查找声明中的类型(declar type)
   - 找到后,再查找实例化的类型(''new'' 后面那个类型,instantiate type)   - 找到后,再查找实例化的类型(''new'' 后面那个类型,instantiate type)
行 940: 行 946:
     - 如果我们想通过该对象访问某些方法,那么这些方法在 declar type 中有没有声明且定义?     - 如果我们想通过该对象访问某些方法,那么这些方法在 declar type 中有没有声明且定义?
 如果上述条件都不满足,Java 编译器会认为该 assignment 是不合法的。 如果上述条件都不满足,Java 编译器会认为该 assignment 是不合法的。
 +==Legal Assignments==
 +Java 会对针对第一个问题进行所谓的 //Relationshio test//:
 +  * 如果 declar type 是 class
 +    * 检查 instantiate type 是不是 declar type 本身,或是其 subclass
 +比如:
 +<code js>
 +//ok, pixy is a Poodle, Poodle is a dog
 +Dog pixy;
 +pixy = new Poodle();
 +
 +//error, pixy is a Dog, a Dog may not be a Poodle
 +Poodle pixy;
 +pixy = new Dog(...);
 +</code>
 +这种 //Is a// 的规则同样应用于将 argument 传递给 parameter 的过程中。这种情况下:
 +  * argument 指向的类型是 object type
 +  * parameter 指向的类型是 instantiate type
 +比如:
 +<code js>
 +public class Human {
 +    public void playFetch(Dog myPet) {
 +        //....
 +    }
 +}
 +Dog richie = new Dog(...);
 +Poodle pixy = new Poodle(...);
 +
 +//ok, richie is a Dog
 +owner.playFetch(richie);
 +//ok, pixy is a Poodle, Poodle is a Dog
 +owner.playFetch(pixy);
 +</code>
 +
 +  * 如果 declar type 是 instance 
 +    * 检查 instantiate type 本身或是其任意父类(祖先)有没有**实现** interface
 +<WRAP center round important 100%>
 +声明为相同 interface 类型的两个对象很可能不是互通的;比如车不能实例化为狗,即便车和狗都有“洗”的行为。
 +</WRAP>
 +==Method Calls==
 +第二个需要检查的是 method 的调用:
 +  * 如果 **declar type** 中包含了该 method,那是没有问题的,比如:
 +<code js>
 +//Canine defines bark()
 +//Poodle defines enterDogShow()
 +
 +//declar type
 +Canine pixy;
 +Poodle richie = new Poodle(...);
 +pixy = richie;
 +
 +//ok, bark() is defined in Canine
 +pixy.bark();
 +
 +//error, Canine doesn't define enterDogShow, even if pixy's instantiate type is Poodle
 +pixy.enterDogShow();
 +</code>
 +<WRAP center round tip 100%>
 +method call **只考虑声明类型**中是否存在该函数的定义,不考虑对象类型;**除非对象类型中存在着该 method 的 overriding**(详情见之后的动态绑定)
 +</WRAP>
 +===Casting===
 +上述例子中,如果我们希望使用 ''pixy'' 直接访问 ''enterDogShow()'',我们需要将其转换为 ''Poodle'' 类型:
 +<code js>
 +//ok, in case of using enterDogshow(), we need casting 
 +// !!!notice the praentheses!!!
 +((Poodle)pixy).enterDogShow();
 +</code>
 +Casting 可以在继承关系树上**向上**,或是**向下**进行(注意不是 is-a 或者 has-a 的关系不行!),得到的结果是一个**临时的**转换后的 Object 类型(的引用)。我们只需要确保 Casting 之后的**类型可以访问被调用的方法**即可。
 +<WRAP center round info 100%>
 +Casting 只是生成了一个**临时的**,对应的 Object 类型的引用。''pixy'' **永远**都是指向 ''Canine'' 类型的引用。
 +</WRAP>
 +
 +==Casting 潜在的问题==
 +Casting 带来了一个问题。来看以下例子:
 +<code js>
 +Canine dg;
 +dg = new Dog(...);
 +
 +//is thi legal?
 +((Poodle)dg).enterDogShow();
 +</code>
 +这种情况下, ''dg'' 的 object type 是 ''Dog'',而不是 ''Poodle''。Casting 是可以进行的;但是在调用 method 的时候,//JVM// 会做 //Is a// 的检测。上面的例子中,我们调用的是 ''Poodle'' 类型实现的 method,在调用之前,编译器会询问:调用者的 object type 是不是 method 实现所在的 object type? (此处就是在问:''Dog'' 是 ''Poodle'' 吗?)如果不是,那该 method call 就无法通过编译。
 +===Dynamic Binding===
 +在检测 method call 是否合法的同时,JVM 还会同时在**运行期**检测哪一个版本最适合当前的 call。处理该调用匹配的过程被称为**动态绑定**(//Dynamic Binding//)。总的来说:
 +  * 如果 object type 和 declar type 中存在不一样的实现(即 object type 中有重写),那么选择 object type 的版本
 +<code js>
 +//if Canine & Poodle both have a bark() implementation
 +Canine pixy;  
 +pixy = new Poodle(...); 
 +
 +//call Poodle.bark()
 +pixy.bark();
 +</code>
 +  * 如果 object type 中不存在重写,JVM 会按照继承树**逐级往上查询**,直到找到对应的实现
 +<code js>
 +//if Canine & Dog both have a bark() implementation, but Poodle dosen't
 +Canine pixy;  
 +pixy = new Poodle(...); 
 +
 +//call Dog.bark()
 +pixy.bark();
 +</code>