1. 多態(tài)
多態(tài)這種特性簡(jiǎn)而言之就是用父類型別的引用指向其子類的實(shí)例览效,然后通過父類的方法調(diào)用實(shí)際子類的成員函數(shù). 先拋結(jié)論: 個(gè)人理解的所謂多態(tài), 本質(zhì)上就是重寫機(jī)制加上向上轉(zhuǎn)型, 重寫就是說子類的同名同參數(shù)的方法可以覆蓋父類的方法, 子類的向上轉(zhuǎn)型消除了多余方法, 就成了多態(tài).
先給出一個(gè)多態(tài)非常簡(jiǎn)單的例子:
Father father = new Child();
這里例子中我們new一個(gè)Child實(shí)例將它丟給father這個(gè)接口, 當(dāng)調(diào)用沒有被重寫的方法時(shí), JVM執(zhí)行父類方法, 當(dāng)調(diào)用被Child重寫過的方法時(shí), 則執(zhí)行子類方法.
2. 多態(tài)的實(shí)現(xiàn)
首先先明確一件事, 多態(tài)=重寫+向上轉(zhuǎn)型, 所以下面關(guān)于的多態(tài)的原理其實(shí)就是重寫的原理.
對(duì)C++了解的同學(xué)應(yīng)該知道C++里面有虛函數(shù)這個(gè)東西, C++為了實(shí)現(xiàn)多態(tài), 在就在實(shí)例對(duì)象里面內(nèi)嵌了虛函數(shù)表vtable, 這個(gè)虛函數(shù)表, 其實(shí)就是一個(gè)存儲(chǔ)了一堆方法指針的表, 來指定調(diào)用的函數(shù), 實(shí)現(xiàn)方法分派. 如果沒有virtual關(guān)鍵字, 那么這個(gè)函數(shù)會(huì)在vtable中保存兩份, 父類的函數(shù)和子類的函數(shù)都會(huì)保存. 用父類引用調(diào)用父類函數(shù), 子類引用調(diào)用子類函數(shù).
Java的vtable和C++大同小異, 最大的區(qū)別在于Java的vtable是在類加載時(shí)動(dòng)態(tài)構(gòu)建的, 大致的邏輯是這樣的:
- 一開始繼承類的vtable中的指針完全指向父類的方法, 也就是說一開始子類的vtable就是父類的vtable
- JVM遍歷子類的所有方法, 找到所有被public或protected關(guān)鍵詞修飾, 并且沒有被static或final修飾的方法, 判斷其方法簽名是否與父類一致, 如果一致說明發(fā)生了重寫, 那么將子類的原本指向父類的方法指針修改為指向自己的方法.
- 如果找出來的方法簽名不一致, 說明子類自己定義了新方法, 那么就把vtable長(zhǎng)度增加1, 并將指針指向新方法
經(jīng)過以上步驟, vtable構(gòu)建完成, 這張表里面分別包含了父類方法(沒重寫的)和子類方法(重寫的), 剩下的事情就順理成章了, 程序在調(diào)用父類引用方法的時(shí)候, JVM會(huì)通過vtable里的指針來調(diào)用方法, 這就是多態(tài).
3. 多態(tài)的好處
多態(tài)可以實(shí)現(xiàn)接口復(fù)用, 隱藏實(shí)現(xiàn)細(xì)節(jié), 降低代碼的復(fù)雜度. 策略模式就是個(gè)最簡(jiǎn)單的例子, 策略模式讓一個(gè)接口實(shí)現(xiàn)多種類, 但是反映在代碼里面真實(shí)使用的就是父類引用Father, 這樣不僅封裝了內(nèi)部細(xì)節(jié), 使代碼更簡(jiǎn)潔. 還增加了可維護(hù)性, 在需要這個(gè)接口或者父類實(shí)現(xiàn)不同功能的時(shí)候只需要將不同的子類向上轉(zhuǎn)型為Father就行了, 簡(jiǎn)潔明了.
這種簡(jiǎn)化分層其實(shí)就是解耦的思想, 解耦的最終目的就是為了使不同功能的代碼放在不同地方, 所謂高內(nèi)聚低耦合, 從而實(shí)現(xiàn)強(qiáng)大的可維護(hù)性, 提升效率. 具體來看, 不論是多態(tài)也好, 依賴注入也好, XML配置也好, 最終目的就是將所有配置和賦值放在一起, 而把核心代碼放在另一個(gè)地方, 當(dāng)需要修改時(shí), 只需要修改配置文件或者實(shí)現(xiàn)不同的接口就可以實(shí)現(xiàn)不同功能
接口復(fù)用讓模塊化或者分層變得簡(jiǎn)單, 在寫一個(gè)大型項(xiàng)目的時(shí)候, 一般來說都需要很多模塊互相調(diào)用. 作為一個(gè)模塊的開發(fā)者我要是不想經(jīng)常讓別人修改代碼, 我就得保持自己寫的接口不變, 來修改下面的實(shí)現(xiàn)類.
進(jìn)一步思考, 這個(gè)時(shí)候想想什么叫做控制反轉(zhuǎn)就顯而易見了, 目的還是為了進(jìn)一步降低耦合度. 對(duì)于一個(gè)組件來說, 開發(fā)者甚至不希望使用者自己來實(shí)現(xiàn)接口, 而是讓使用者加個(gè)注解, 我自己來幫你實(shí)現(xiàn), 優(yōu)點(diǎn)當(dāng)然是顯而易見的, 組件的實(shí)現(xiàn)類甚至連名字都可以自由更換.