Overview
看到書中的例子我以爲我都懂了,不過看到了一個例子之後,才發現我都是半懂,沒有仔細思考,看書的時候心不靜。關於多態有一個優先級可以概括,詳見http://blog.csdn.net/thinkghoster/archive/2008/04/19/2307001.aspx。優先級由高到低依次爲:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
Example and research
class A {
public String show(D obj){
return ("A and D");
}
public String show(A obj){
return ("A and A");
}
}
class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{}
class D extends B{}
以下是結果
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b)); ①
System.out.println(a1.show(c)); ②
System.out.println(a1.show(d)); ③
System.out.println(a2.show(b)); ④
System.out.println(a2.show(c)); ⑤
System.out.println(a2.show(d)); ⑥
System.out.println(b.show(b)); ⑦
System.out.println(b.show(c)); ⑧
System.out.println(b.show(d)); ⑨
其中4,5,6,9全部做錯。那就一個一個驗證這個優先級吧。
System.out.println(a2.show(b))
之前看過書中有這樣的例子:
import java.util.*;
class Shape {
void draw() {System.out.println("Shape.draw()");}
void erase() {}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle.draw()");
}
void erase() {
System.out.println("Circle.erase()");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square.draw()");
}
void erase() {
System.out.println("Square.erase()");
}
}
class Triangle extends Shape {
void draw() {
System.out.println("Triangle.draw()");
}
void erase() {
System.out.println("Triangle.erase()");
}
}
// A "factory" that randomly creates shapes:
class RandomShapeGenerator {
private Random rand = new Random();
public Shape next() {
switch(rand.nextInt(3)) {
default:
case 0: return new Circle();
case 1: return new Square();
case 2: return new Triangle();
}
}
}
public class Shapes {
private static RandomShapeGenerator gen =
new RandomShapeGenerator();
public static void main(String[] args) {
Shape[] s = new Shape[9];
// Fill up the array with shapes:
for(int i = 0; i < s.length; i++)
s[i] = gen.next();
// Make polymorphic method calls:
for(int i = 0; i < s.length; i++){
s[i].draw();
};
}
}
結果如下:
Square.draw()
Circle.draw()
Triangle.draw()
Triangle.draw()
Triangle.draw()
Circle.draw()
Square.draw()
Triangle.draw()
Circle.draw()
每一個父類Shape引用的都是子類,輸出的也都是子類的方法。所以我以爲對於4中應該輸出"B AND B"。但是注意到調用的方法在父類中一個在父類中定義過了,一個沒有。什麼意思?如果在類A中加上這麼一段:
public String show(B obj){
return ("A and BB");
}
那麼4的結果就是"B AND B"。這是類A和B中都有一個同樣的方法show(B obj)。那爲什麼4的結果是"B AND A"了?注意到剛纔說的優先級,首先查找this.show(),這是是沒有對應的方法的。接着查找super.show(),super.show是沒有的,因爲a2的定義是A a2 = new B(),a2還是一個類型爲a的引用變量。接着查找this.show((super)O),這樣就找到了對應的方法了,爲類A中的show(A object)方法,但是注意子類B中有方法把他給覆蓋了!所以使用的類B中的方法!結果爲"B and A"。
其他的都同理可以論證了。
My thinking in java
加上了多態後,立刻變的複雜了。從面向過程轉到面向對象我還非常不習慣,習慣於參數必須和聲明的參數類型完全一樣。有了多態和抽象類到底有什麼好處,爲什麼要引入這兩個東西了?
- 抽象類
我覺得抽象類主要是提高代碼的可讀性,如果沒有抽象類也一樣可以實現這樣的功能。比如在父類中的方法實現就寫爲空,然後在子類中覆蓋就行了,加上了abstract之後只不過加上了語法的檢查。
- 多態和繼承
多態是一個利器,不過很有可能讓代碼變的更糟糕。父類和子類有三種情況:
- 父類方法比子類多
這種情況下公共的接口最好用abstract,父類中多出來的方法,作爲所有子類的公共方法使用設置爲Public讓子類繼承。
- 父類方法和子類一樣多
這個時候最好父類的方法都用來繼承,提供公共的接口。
- 父類方法少於子類
相同的部分作爲公共接口,子類中多出來的方法可以用downcasting。
這樣定義類時都可以用父類的引用使用子類方法。