在優銳課架構學習中,瞭解了關於用多態方法調用將你的大腦包圍在Java方法調用周圍
根據傳說中的Venkat Subramaniam,多態是面向對象編程中最重要的概念。 多態性(或對象根據其類型執行特殊操作的能力)使Java代碼具有靈活性。 諸如 四個人幫 之類的設計模式(例如Command,Observer,Decorator,Strategy和許多其他模式)都使用某種形式的多態性。 精通此概念可極大地提高你思考解決編程難題的能力。
Juggy:
public abstract class JavaMascot {
public abstract void executeAction();}public class Duke extends JavaMascot {
@Override
public void executeAction() {
System.out.println("Punch!");
}}public class Juggy extends JavaMascot {
@Override
public void executeAction() {
System.out.println("Fly!");
}}public class JavaMascotTest {
public static void main(String... args) {
JavaMascot dukeMascot = new Duke();
JavaMascot juggyMascot = new Juggy();
dukeMascot.executeAction();
juggyMascot.executeAction();
}}
Punch!Fly!
多態性的接口和繼承
藉助此Java Challenger,我們將致力於多態性與繼承之間的關係。 要記住的主要事情是多態性需要繼承或接口實現。 你可以在下面的示例中看到這一點,其中包括Duke和
由於其特定的實現方式,Duke和Juggy的動作都將被執行。
方法是否重載了多態性?
許多程序員對多態與方法重寫和方法重載之間的關係感到困惑。 實際上,只有方法重載纔是真正的多態性。 重載使用相同的方法名稱,但參數不同。 多態性是一個廣義術語,因此始終會有關於該主題的討論。
多態性的目的是什麼?
使用多態的最大優點和目的是將客戶端類與實現代碼分離。 客戶端類無需進行硬編碼,而是接收實現以執行必要的操作。 這樣,客戶端類就足夠了解執行其動作的知識,這是鬆耦合的示例。
爲了更好地瞭解多態的目的,請看一下
public abstract class SweetProducer {
public abstract void produceSweet();}public class CakeProducer extends SweetProducer {
@Override
public void produceSweet() {
System.out.println("Cake produced");
}}public class ChocolateProducer extends SweetProducer {
@Override
public void produceSweet() {
System.out.println("Chocolate produced");
}}public class CookieProducer extends SweetProducer {
@Override
public void produceSweet() {
System.out.println("Cookie produced");
}}public class SweetCreator {
private List<SweetProducer> sweetProducer;
public SweetCreator(List<SweetProducer> sweetProducer) {
this.sweetProducer = sweetProducer;
}
public void createSweets() {
sweetProducer.forEach(sweet -> sweet.produceSweet());
}}public class SweetCreatorTest {
public static void main(String... args) {
SweetCreator sweetCreator = new SweetCreator(Arrays.asList(new CakeProducer(),
new ChocolateProducer(), new CookieProducer()));
sweetCreator.createSweets();
}}
在此示例中,你可以看到SweetCreator類僅知道SweetProducer類。 它不知道每個甜食的實現。 這種分離使我們可以靈活地更新和重用我們的類,並使代碼易於維護。 設計代碼時,請始終尋找使代碼儘可能靈活和可維護的方法。 多態性是用於這些目的的一種非常強大的技術。
提示:@Override註釋使程序員有義務使用必須重寫的相同方法簽名。 如果未重寫該方法,則將出現編譯錯誤。
方法覆蓋中的協變返回類型
如果它是協變類型,則可以更改覆蓋方法的返回類型。 協變量類型基本上是返回類型的子類。 考慮一個例子:
public abstract class JavaMascot {
abstract JavaMascot getMascot();}public class Duke extends JavaMascot {
@Override
Duke getMascot() {
return new Duke();
}}
由於Duke是JavaMascot,因此我們可以在覆蓋時更改返回類型。
Java核心類的多態性
我們一直在覈心Java類中使用多態。 一個非常簡單的示例是當我們實例化ArrayList類時,將List接口聲明爲一種類型:
List<String> list = new ArrayList<>();
爲了進一步講解,請考慮使用Java Collections API且無多態性的以下代碼示例:
public class ListActionWithoutPolymorphism {
// Example without polymorphism
void executeVectorActions(Vector<Object> vector) {/* Code repetition here*/}
void executeArrayListActions(ArrayList<Object> arrayList) {/*Code repetition here*/}
void executeLinkedListActions(LinkedList<Object> linkedList) {/* Code repetition here*/}
void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList<Object> copyOnWriteArrayList)
{ /* Code repetition here*/}}public class ListActionInvokerWithoutPolymorphism {
listAction.executeVectorActions(new Vector<>());
listAction.executeArrayListActions(new ArrayList<>());
listAction.executeLinkedListActions(new LinkedList<>());
listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList<>());}
醜陋的代碼,不是嗎? 想象一下要維護它! 現在來看具有多態性的相同示例
public static void main(String … polymorphism) {ListAction listAction = new ListAction();
listAction.executeListActions();}public class ListAction {
void executeListActions(List<Object> list) {
// Execute actions with different lists
}}public class ListActionInvoker {
public static void main(String... masterPolymorphism) {
ListAction listAction = new ListAction();
listAction.executeListActions(new Vector<>());
listAction.executeListActions(new ArrayList<>());
listAction.executeListActions(new LinkedList<>());
listAction.executeListActions(new CopyOnWriteArrayList<>());
}}
多態性的好處是靈活性和可擴展性。 除了創建幾種不同的方法外,我們可以只聲明一個接收通用List類型的方法。
在多態方法調用中調用特定方法
可以在多態調用中調用特定的方法,但是這樣做會犧牲靈活性。 這是一個例子:
public abstract class MetalGearCharacter {
abstract void useWeapon(String weapon);}public class BigBoss extends MetalGearCharacter {
@Override
void useWeapon(String weapon) {
System.out.println("Big Boss is using a " + weapon);
}
void giveOrderToTheArmy(String orderMessage) {
System.out.println(orderMessage);
}}public class SolidSnake extends MetalGearCharacter {
void useWeapon(String weapon) {
System.out.println("Solid Snake is using a " + weapon);
}}public class UseSpecificMethod {
public static void executeActionWith(MetalGearCharacter metalGearCharacter) {
metalGearCharacter.useWeapon("SOCOM");
// The below line wouldn't work
// metalGearCharacter.giveOrderToTheArmy("Attack!");
if (metalGearCharacter instanceof BigBoss) {
((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!");
}
}public static void main(String... specificPolymorphismInvocation) {
executeActionWith(new SolidSnake());
executeActionWith(new BigBoss());
}}
我們在這裏使用的技術是廣播或在運行時故意更改對象類型。
請注意,只有在將通用類型轉換爲特定類型時纔可以調用特定方法。 一個很好的類比是對編譯器明確地說:“嘿,我知道我在這裏做什麼,所以我將對象轉換爲特定類型並使用特定方法。”
參考上面的示例,一個重要原因是編譯器拒絕接受特定的方法調用:正在傳遞的類可能是SolidSnake。 在這種情況下,編譯器無法確保MetalGearCharacter的每個子類都聲明瞭giveOrderToTheArmy方法。
保留關鍵字的實例
請注意實例化的保留字。 在調用特定方法之前,我們詢問了MetalGearCharacter是否爲BigBoss的“實例”。 如果不是BigBoss實例,我們將收到以下異常消息:
線程“主”中的異常java.lang.ClassCastException:com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake無法轉換爲com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss
超級保留關鍵字
如果我們想引用Java超類的屬性或方法怎麼辦? 在這種情況下,我們可以使用超級保留字。 例如:
public class JavaMascot {
void executeAction() {
System.out.println("The Java Mascot is about to execute an action!");
}}public class Duke extends JavaMascot {
@Override
void executeAction() {
super.executeAction();
System.out.println("Duke is going to punch!");
}
public static void main(String... superReservedWord) {
new Duke().executeAction();
}}
在Duke的executeAction方法中使用保留字super調用超類方法。 然後我們從杜克(Duke)執行特定操作。 因此,我們可以在下面的輸出中看到兩條消息:
Java Mascot即將執行一個動作!Duke要出拳了!
接受多態挑戰!
讓我們嘗試一下你對多態性和繼承的瞭解。首先,請仔細分析以下代碼:
public class PolymorphismChallenge {
static abstract class Simpson {
void talk() {
System.out.println("Simpson!");
}
protected void prank(String prank) {
System.out.println(prank);
}
}
static class Bart extends Simpson {
String prank;
Bart(String prank) { this.prank = prank; }
protected void talk() {
System.out.println("Eat my shorts!");
}
protected void prank() {
super.prank(prank);
System.out.println("Knock Homer down");
}
}
static class Lisa extends Simpson {
void talk(String toMe) {
System.out.println("I love Sax!");
}
}
public static void main(String... doYourBest) {
new Lisa().talk("Sax :)");
Simpson simpson = new Bart("D'oh");
simpson.talk();
Lisa lisa = new Lisa();
lisa.talk();
((Bart) simpson).prank();
}}
你怎麼看? 最終輸出將是什麼? 不要使用IDE來解決這個問題! 關鍵是要提高代碼分析技能,因此請嘗試自己確定輸出。
瞭解多態
對於以下方法調用:
new Lisa().talk("Sax :)");
輸出爲“ I love Sax!”,這是因爲我們正在向該方法傳遞字符串,而Lisa具有該方法。
對於下一個調用:
Simpson simpson = new Bart("D'oh");
simpson.talk();
輸出將是“Eat my shorts!” 這是因爲我們正在使用Bart實例化Simpson類型。
現在檢查一下,這有點棘手:
Lisa lisa = new Lisa();
lisa.talk();
在這裏,我們使用帶有繼承的方法重載。 我們沒有將任何內容傳遞給talk方法,這就是調用Simpsontalk方法的原因。 在這種情況下,輸出將是:
"Simpson!"
這裏還有一個:
((Bart) simpson).prank();
在這種情況下,當我們使用new Bart(“ D'oh”);實例化Bart類時,會傳遞惡作劇字符串。 在這種情況下,首先將調用super.prank方法,然後是來自Bart的特定惡作劇方法。 輸出將是:
"D'oh""Knock Homer down"
視頻挑戰! 調試Java多態性和繼承
調試是完全吸收編程概念並改善代碼的最簡單方法之一。 在此視頻中,你可以在調試和解釋Java多態性挑戰時繼續學習:
多態性的常見錯誤
認爲無需使用強制轉換就可以調用特定方法是一個常見的錯誤。
另一個錯誤是不確定多態實例化類時將調用哪種方法。 請記住,要調用的方法是創建的實例的方法。
還要記住,方法重載不是方法重載。
如果參數不同,則無法覆蓋方法。 如果返回類型是超類方法的子類,則可以更改重寫方法的返回類型。
喜歡這篇文章的可以點個贊,歡迎大家留言評論,記得關注我,每天持續更新技術乾貨、職場趣事、海量面試資料等等
> 如果你對java技術很感興趣也可以交流學習,共同學習進步。
不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代
文章寫道這裏,歡迎完善交流。最後奉上近期整理出來的一套完整的java架構思維導圖,分享給大家對照知識點參考學習。有更多JVM、Mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java乾貨
關於多態性要記住什麼
創建的實例將確定使用多態時將調用哪種方法。
@ Override註釋使程序員有義務使用覆蓋的方法; 否則,將出現編譯器錯誤。
多態可以與普通類,抽象類和接口一起使用。
大多數設計模式取決於某種形式的多態性。
在多態子類中使用特定方法的唯一方法是使用強制轉換。
可以使用多態性在代碼中設計功能強大的結構。
運行測試。 這樣做,你將能夠掌握這個強大的概念!