• 沒有找到結果。

繼承與多型

N/A
N/A
Protected

Academic year: 2021

Share "繼承與多型"

Copied!
78
0
0

加載中.... (立即查看全文)

全文

(1)
(2)

繼承與多型

學習目標

• 瞭解繼承目的

• 瞭解繼承與多型的關係

• 知道如何重新定義方法

• 認識 java.lang.Object

• 簡介垃圾收集機制

(3)

繼承共同行為

• 假設你在正開發一款 RPG ( Role-playing

game )遊戲,一開始設定的角色有劍士與魔 法師

(4)
(5)

繼承共同行為

• 如果要改進,可以把相同的程式碼提昇

( Pull up )為父類別…

(6)
(7)

繼承共同行為

• 接著 SwordsMan 可以如下繼承 Role :

(8)

繼承共同行為

• Magician 也可以如下定義繼承 Role 類別

(9)

繼承共同行為

• 如何看出確實有繼承了呢?

(10)

多型與 is-a

• 子類別只能繼承一個父類別

• 繼承可避免類別間重複的行為定義

• 子類別與父類別間會有 is-a 的關係

– SwordsMan 是一種 Role ( SwordsMan is a Role )

– Magician 是一種 Role ( Magician is a Rol e )

(11)

多型與 is-a

• 要開始理解多型( Polymorphism ),必須先 知道你操作的物件是「哪一種」東西

• 可以通過編譯:

(12)

多型與 is-a

• 以下的程式片段也可以通過編譯?

• 以下的程式片段為何無法通過編譯呢?

(13)

多型與 is-a

• 將自己當作編譯器,從 = 號右邊往左讀:右 邊是不是一種左邊呢(右邊型態是不是左邊 型態的子類別)?

(14)

多型與 is-a

• 以下編譯失敗:

• 以下的程式片段是否可以通過編譯:

(15)

多型與 is-a

• 如果你不想要編譯器囉嗦,可以叫它住嘴:

• 執行時期並不會出錯

(16)

多型與 is-a

• 以下的程式片段編譯可以成功:

• 執行時期發生錯誤

(17)

多型與 is-a

• 以下編譯成功,執行也沒問題:

• 以下程式片段會編譯失敗:

(18)

多型與 is-a

• 以下程式片段編譯成功,執行時也沒問題:

• 以下程式片段編譯成功,但執行時拋出 ClassCastException :

(19)

多型與 is-a

• 好像只是在玩弄語法?

• 設計 static 方法,顯示所有角色的血量 …

(20)

多型與 is-a

• 重載方法的運用

• 如果有一百個角色呢?重載出一百個方法?

(21)

多型與 is-a

• 這些角色都是一種 Role

(22)

多型與 is-a

• 什麼叫多型?以抽象講法解釋,就是使用單 一介面操作多種型態的物件!

• 若用以上的範例來理解,在 showBlood() 方法中,既可以透過 Role 型態操作

SwordsMan 物件,也可以透過 Role 型態操 作 Magician 物件。

(23)

重新定義行為

• 請設計 static 方法,可以播放角色攻擊動

(24)

重新定義行為

對 drawFight() 方法而言,只知道傳進來 的會是一種 Role 物件,所以編譯器也只能 檢查你呼叫的方法, Role 是不是有定義

• 仔細觀察一下 SwordsMan 與 Magician 的 fight() 方法的方法簽署( method

signature ) …

(25)

重新定義行為

將 fight() 方法提昇至 Role 類別中定義:

(26)

重新定義行為

(27)

重新定義行為

• 如果傳入 fight() 的是

SwordsMan , role 參數參考的就是

SwordsMan 實例,操作的就是 SwordsMan 上的方法定義:

(28)

重新定義行為

• 如果傳入 fight() 的是 Magician , role 參數參考的就是 Magician 實例,操作的就 是 Magician 上的方法定義:

(29)

重新定義行為

• 在重新定義父類別中某個方法時,子類別必 須撰寫與父類別方法中相同的簽署

• 如果疏忽打錯字了…

(30)

重新定義行為

在 JDK5 之後支援標註( Annotation )

• @Override 要求編譯器檢查,該方法是不 是真的重新定義了父類別中某個方法

(31)

抽象方法、抽象類別

• 上一個範例中 Role 類別的定義

中, fight() 方法區塊中實際上沒有撰寫 任何程式碼

• 沒有任何方式強迫或提示子類別一定要實作 fight() 方法

(32)

抽象方法、抽象類別

• 如果某方法區塊中真的沒有任何程式碼實作,可以 使用 abstract 標示該方法為抽象方法( Abstract method )

• 內含抽象方法的類別,一定要在 class 前標示

abstract ,如上例所示,這表示這是一個定義不 完整的抽象類別( Abstract class )

(33)

抽象方法、抽象類別

• 如果嘗試用抽象類別建構實例,就會引發編 譯錯誤:

(34)

抽象方法、抽象類別

• 子類別如果繼承抽象類別,對於抽象方法有 兩種作法

– 繼續標示該方法為 abstract (該子類別因此也 是個抽象類別,必須在 class 前標示

abstract ) – 實作抽象方法

• 兩個作法都沒實施,就會引發編譯錯誤:

(35)

protected 成員

• 上一節的 RPG 遊戲來說,如果建立了一個角 色,想顯示角色的細節,必須如下撰寫:

(36)

protected 成員

• 可以在 SwordsMan 或 Magician 上定義個 toString() 方法,傳回角色的字串描述:

(37)

protected 成員

• 客戶端就可以如下撰寫:

• 不過因為 Role 中的 name 、 level 與 blood 被定義為 private ,所以無法直接於子類別中 存取,只能透過 getName() 、 getLevel()

、 getBlood() 來取得

(38)

protected 成員

• 只想讓子類別可以直接存取 name 、 level 與 blood 的話,可以定義它們為

protected :

(39)

protected 成員

(40)

重新定義的細節

• 有時候重新定義方法時,並非完全不滿意父 類別中的方法,只是希望在執行父類別中方 法的前、後作點加工

(41)

重新定義的細節

• 如果想取得父類別中的方法定義,可以於呼 叫方法前,加上 super 關鍵字

(42)

重新定義的細節

• 可以使用 super 關鍵字呼叫的父類別方法,

不能定義為 private

• 對於父類別中的方法權限,只能擴大但不能 縮小

– 若原來成員 public ,子類別中重新定義時不可 為 private 或 protected

(43)

重新定義的細節

在 JDK5 之前…

(44)

重新定義的細節

在 JDK5 之後,重新定義方法時,如果返回 型態是父類別中方法返回型態的子類別,也 是可以通過編譯的,圖 6.11 的例子,在

JDK5 中並不會出現編譯錯誤

(45)

再看建構式

• 在建構子類別實例後,會先進行父類別定義 的初始流程,再進行子類別中定義的初始流

• 也就是建構子類別實例後,會先執行父類別 建構式定義的流程,再執行子類別建構式定 義的流程

(46)

再看建構式

• 如果子類別建構式中沒有指定執行父類別中 哪個建構式,預設會呼叫父類別中無參數建 構式

(47)

再看建構式

• 如果想執行父類別中某建構式,可以使用

super() 指定:

(48)

再看建構式

• 當你這麼撰寫時:

(49)

再看建構式

• 等於你這麼撰寫:

(50)

再看建構式

• 知道以下為什麼會編譯錯誤嗎?

(51)

再看 final 關鍵字

• 如果在指定變數值之後,就不想再改變變數 值,可以在宣告變數時加上 final 限定

• 如果物件資料成員被宣告為 final ,但沒有 明確使用 = 指定值,那表示延遲物件成員值 的指定,在建構式執行流程中,一定要有對 該資料成員指定值的動作

(52)

再看 final 關鍵字

• 如果 class 前使用了 final 關鍵字定義,

那麼表示這個類別是最後一個了,不會再有 子類別,也就是不能被繼承

– String 在定義時就限定為 final

(53)

再看 final 關鍵字

• 打算繼承 final 類別,則會發生編譯錯誤:

(54)

再看 final 關鍵字

• 定義方法時,也可以限定該方法為 final , 這表示最後一次定義方法了,也就是子類別 不可以重新定義 final 方法

– java.lang.Object 上有幾個 final 方法

(55)

再看 final 關鍵字

• 如果你嘗試在繼承父類別後,重新定義 final 方法,則會發生編譯錯誤:

(56)

java.lang.Object

• 定義類別時沒有使用 extends 關鍵字指定 繼承任何類別,則繼承

java.lang.Object

(57)

java.lang.Object

• Java 中所有物件,一定「是一種」 Object

• 如果有個需求是使用陣列收集各種物件,那 該宣告為什麼型態呢?答案是 Object[] !

(58)

java.lang.Object

• 以下定義的 ArrayList 類別,可以不限長 度地收集物件:

(59)

java.lang.Object

(60)
(61)

java.lang.Object

• java.lang.Object 是所有類別的頂層父 類別

• Object 上定義的方法,所有物件都繼承下 來了,只要不是被定義為 final 的方法,都 可以重新定義

(62)
(63)

java.lang.Object

• Object 的 toString() 預設定義為:

• 6.2.1 的範例中, SwordsMan 等類別,是重 新定義了 toString()

• 許多方法若傳入物件,預設都會呼叫 toString()

– 例如 System.out.print() 等方法

(64)

java.lang.Object

• 6.2.1 的這個程式片段:

(65)

java.lang.Object

在 Java 中要比較兩個物件的實質相等性,並 不是使用 == ,而是透過 equals() 方法

• equals() 方法是 Object 類別就定義的方

(66)
(67)

java.lang.Object

• instanceof 運算子可以用來判斷物件是否 由某個類別建構,左運算元是物件,右運算 元是類別

• 編譯器會檢查左運算元型態是否在右運算元 型態的繼承架構

(68)

java.lang.Object

• 執行時期,並非只有左運算元物件為右運算 元類別直接實例化才傳回 true ,只要左運 算元型態是右運算元型態的子類

型, instanceof 也是傳回 true

(69)

關於垃圾收集

• JVM 有垃圾收集( Garbage Collection, GC ) 機制,收集到的垃圾物件所佔據的記憶體空 間,會被垃圾收集器釋放

• 執行流程中,無法透過變數參考的物件,就 是 GC 認定的垃圾物件

(70)

關於垃圾收集

• 假設你有一個類別:

(71)

關於垃圾收集

(72)

關於垃圾收集

(73)

關於垃圾收集

(74)

關於垃圾收集

(75)

再看抽象類別

• 開發一個猜數字遊戲 …

• 老闆皺著眉頭說:「我有說要在文字模式下執行這 個遊戲嗎?」

(76)

再看抽象類別

(77)

再看抽象類別

(78)

再看抽象類別

參考文獻

相關文件

以下 Java 程式執行完後,輸出結果為何?(A)無法編譯,因為 Rectangle 類別不能同時 extends 一個類別且 implemets 一個介面(B)無法編譯,因為 Shapes 類別沒有

甲方為未成年人時,應具備甲方法定代理人同意書及其年齡證明文

(2) 「唯以分別安立」 ,表示對對象觀察透過分別心找出「性質」 「定

好了既然 Z[x] 中的 ideal 不一定是 principle ideal 那麼我們就不能學 Proposition 7.2.11 的方法得到 Z[x] 中的 irreducible element 就是 prime element 了..

利用這種說法一個 vector space 的 一組 basis 利用 Proposition 1.5.4 便可以說成就是該 vector space 中所有 spanning set 的 minimal element; 也可以說是該 vector

數學上有很多的定義,也有很多定理,定理是必須經過證明才能確立的事

以無條件捨去法取㉃萬位數,用無條件捨去 法取概數㉃萬位,也就是㈲幾個萬,未滿萬 位的數都不用算,也就是捨去,因此定位板.

是究竟的了義說,這才是佛法的心髓。龍樹說:「三悉檀可破可壞,第一義悉 檀不可壞」。 ……