多型(Polymorphism)的底層真相!
就像「戴上什麼濾鏡看它,你就只能把它當成什麼來用」。
一句話記住左邊的意義
「左邊決定了你的合法身分證與操作介面,哪怕你骨子裡是隻十八般武藝樣樣精通的豬,只要套上食材的殼,你就只能老老實實當一塊肉!」
System.out.println(food.youMayEat());
當程式正式跑起來時,背後發生的事情是這樣的:
看左邊(有這道菜):
門口店員檢查過點菜單,確認「食材(Edible)」確實有 youMayEat() 這個合格項目,准許放行。
看右邊(誰在接單):
電腦順著記憶體地址發現裡面有一隻實體小豬!
跑子類別覆寫後的專屬版本:
介面原本只空泛地開出「可食用」的空頭支票,但小豬在當初簽約時早就用 @Override 把大括號補滿,清清楚楚寫下了「怎麼吃」的具體作法。於是後台直接端出小豬特製的招牌菜:
You may eat pork chops.(可以做成美味的炸豬排)
一句話總結動態綁定(Dynamic Binding):
左邊(你有沒有這項功能),右邊(跑你覆寫後的最新版本)!」
搞懂了左右兩邊,會想:為什麼非要用老爸的規格去接小孩?老老實實寫 Dog d = new Dog(); 不香嗎?
想像你在寫一個農場管理系統,如果沒有通用性,你的記憶體就像擺滿了獨立小抽屜:
Dog[] dogs;
Cat[] cats;
Pig[] pigs;
狗有狗的專屬箱子,貓有貓的專屬箱子。巡邏時,你得針對狗寫一次迴圈、貓寫一次迴圈、豬再寫一次迴圈;未來只要農場多引進一隻鴨子,你整份系統就要重掀開來多塞一個抽屜。」
這時候你靈機一動:「我幹嘛非得把牠們分得這麼細?退一步看,牠們在生物學上不都是動物嗎?」
把具體的小孩(狗、貓、豬),往上看作通用的老爸(動物),並整批塞進名為「動物」大箱子(陣列)裡,這個動作,就叫做向上轉型:
向上轉型(左邊):大家都掛上「動物識別證」裝進同一箱。既然識別證上寫著大家都是 Animal,而全天下動物都「會叫有 makeSound() 規格」。動態執行(右邊):迴圈一圈一圈跑下去,就像點名官拿著麥克風依序遞到牠們嘴邊:遞給第一隻:本體是 Dog,觸發小狗覆寫的大括號「汪汪!」遞給第二隻:本體是 Cat,觸發貓咪覆寫的大括號 「喵喵~」遞給第三隻:本體是 Pig,觸發小豬覆寫的大括號 「哼哼!」點名官(外部程式)根本不需要在乎現在站在面前的是誰,他只要喊「叫一聲!」,底下的小動物就會自動翻出各自大括號裡的台詞演出。這就是多型最省力的地方!