polymorphism
多型, 是指著一個物件有著多重的身份, 比如小明這個人, 在學校是個學生, 在家裏是爸媽的小孩, 他同時也是老婆的丈夫, 他小孩的爸爸.
那多型在物件導向裏有什麼用處呢, 先看下面的代碼
public class Test { public static void main(String[] args){ Pikachu [] pk=new Pikachu[10]; pk[0]=new Pikachu(); //pk[1]=new Eve(); error Pokemon []po=new Pokemon[10]; po[0]=new Pikachu(); po[1]=new Eve(); } } class Pokemon{} class Pikachu extends Pokemon{} class Eve extends Pokemon{}
pk是Pikachu的陣列, 所以只能容納皮卡丘物件, 當Eve要放進去時就會產生編譯時期錯誤.
po是Pokemon的陣列, 只陣列可以容納皮卡丘及Eve的物件, 只要是繼承Pokemon的物件都可以放進去.
子類別擴充功能
Pikachu繼承了Pokemon後, 可以自行新增Pokemon沒有的新功能, 比如lighting()閃電攻擊.
public class Test {
public static void main(String[] args){
Pokemon p1=new Pikachu();
Pikachu p2=new Pikachu();
//p1.lightint();
p2.lighting();
((Pikachu)p1).lighting();
}
}
class Pokemon{}
class Pikachu extends Pokemon{
public void lighting(){
System.out.println("使用十萬伏特閃電攻擊");
}
}
class Eve extends Pokemon{}
父子關係
Pikachi類別是Pokemon的子類別, 所以Pikachi有著 “is a ” Pokemon的關係(皮卡丘是一隻神奇寶貝)
父類別參考
上面代碼中, p1是皮卡丘, 但其型態是Pokemon, 使用了父類別參考. p2也是皮卡丘, 然後使用Pikachu自已本身的參考. 這在現實世界中是合理的, 因為皮卡丘就是皮卡丘, 皮卡丘也是神奇寶貝.
但當使用父類別參考物件時, 只能使用父類別的方法,子類別新增的方法一完全失效. 因為p1雖是皮卡丘, 但他是以神奇寶貝稱呼著. 然而並不是所有的神奇寶貝都會有閃電攻擊, 所以p1的lighting() 當然會失效.
強制轉換
p1若硬要使用lighting(), 則必需先進行強制轉換(casting).
((Pikachu)p1).lighting();
直接使用 p1.lighting(), 就會出現Compile fail. 但經過((Pikachi)p1)轉型後, 就可以把對手電的金係係了
虛擬方法調用(Virtual method invocation)
子類別物件使用父類別參考, 由物件調用覆蓋方法時, 還是會調用子類別的方法, 而不是父類別的方法
爛梨假蘋果
即然Pokemon p1可以強制轉成Pikachu, 那下面代碼的eve, 是否也可以轉成Pikachu呢? Yes, 百分之百肯定的, 這在編譯時不會出現錯誤. 但在執行時期, 會因為eve強制轉換成Pikachu時轉不過去而發生例外並造成程式閃退. 這現象叫爛梨假蘋果
Pokemon eve=new Eve(); ((Pikachu)eve).lighting(); //ok, 沒問題
instanceof 運算子
為了解決上面閃退的問題, 就必需搬出instanceof. instanceof是一個不使用符號代替的特殊運算子, 可以用在判斷是否屬於某個類別. 所以程式碼改寫成如下就不會閃退
Pokemon eve=new Eve(); if(eve instanceof Pikachu) ((Pikachu)eve).lighting(); else System.out.println("爛梨假蘋果");
標註介面(Marker Interface)
public class Person implements java.io.Serializable{}, 只是標註Person是屬於Serializable型態, 不需要實作任何方法
Object 物件
所有的類別都是繼承Object類別, 所以所有類別的最上面階層是Object類別, 所有類別都可以使用Object裏的方法. 但Object並沒有太多的方法
Object最常用的是toString(), 不過大部份的類別都會覆載toString()
Which methods of an object can be accessed via an interface that is implements? A. All the methods implemented in the object's class B. All the methods implemented in the object's superclass C. The methods declared in the interface Answer : C
多型取代重載
下面是一般重載的寫法, 依不同的職級發放不同的股票
class EmployeeStockPlan{ public int grantStock(Manager m){} public int grantStock(Engineer e){} public int grantStock(Admin a){} }
若改成下面的方式後, 傳入grantStock()的參數, 會依不同的職級跑不同的方法.
class Employee{ protected int calsulateStock(){return 10;} } class Manager extends Employee{ public int calculateStock(){return 20;} } class EmployeeStockPlan{ public int grantStock(Employee e){ return e.calsulateStock(); } }
轉型規則
升級(upward)往父類別升
降級(downward)往子類別降
以上為升級(upward)
Employee e=m; <==ok;
Manager m=d; <==ok;
以上是降級(downward)
Manager m=(Manager) e; <==ok
Director d=(Director)m; <==error