CS61B資料結構及演算法筆記:(六)多型

Evan
4 min readDec 10, 2021

--

從型態確認的過程可以發現變數在不同時候有著不同的型態,可以是類別本身(實例化)的型態、父類別的型態、亦或是父父類別的型態,這又稱:多型(Polymorphism),接下來會以實際的案例介紹多型的應用。

假設我們想要建立一個函數 max 能夠從存放任意物件的陣列中,取出最大的物件,如下所示。不過這段程式碼在編譯階段(compile time)會發生錯誤,因為不是每一種物件都支援大於(>)的符號來比較大小,即使實例的物件是 int 型態,Java 還是會確認,是否每一個 Object 都可以使用該符號。

為了比較存放 Dog 的陣列,其中一個方法為將 max 改寫為客制的 maxDog,並將參數型態及回傳的型態改為 Dog,雖然暫時可以解決這個問題,不過卻不是我們想要的目標:在"任意"物件陣列中取出最大的物件。

為了讓所有的物件都能夠支援大於的符號,換句話說,可以比較大小,此時就需要介面(interface)的幫忙。首先將想要比較的類別繼承一個介面 OurComparable,該介面擁有一個 compareTo 的函數,令繼承該介面的子類別都需要實現比較物件的方法,並回傳一個整數為 0、1、-1,分別代表等於、大於、小於三種不同個狀況。

在這個範例中,因 Dog 繼承了 OurComparable 介面,必須實現 compareTo 方法,並且以 Dog 的 size 屬性做為比較的依據,如下所示。

繼承 OurComparable 的類別 Dog 目前已能藉由 size,使用方法 compareTo 和其他 Dog 比較大小,雖然並非字面上可以支援大於這個符號,不過實質上已經能和其他 Dog 比較大小。此時只需要將 max 中的參數及回傳值的型態改成 OurComparable,並將比較大小的方式改為使用方法 compareTo,如下所示即可讓所有繼承 OurComparable 的類別,都可以使用方法 Maximizer.max()。

普遍上會將 Dog 內 compareTo 的 if else 判斷式,改寫為直接回傳

return this.size -uddadog.size;

以簡化程式碼。

到目前為止,只要類別繼承了 OurComparable,都可以使用方法 max 來比較大小。不過還有兩個缺點需要改進

  1. 在方法 compareTo 裡面需要使用 cast 將 Object 轉化為該類別。
  2. 如果要在其他類別使用自製的 max 方法,需重新繼承 OurComparable。

實際上 Java 已提供一個內建介面 Comparable<T>,提供許多存在這個世界上的類別來繼承,和我們自製的 OurComparable 差異在於 Comparable 使用通型<T>,讓方法 compareTo 內的參數型態並非 Object,而是任意型態。

繼承了 Cmoparable<T> 的 Dog 只要將通型 <T> 更改為 <Dog>,方法 compareTo 就不須要再將 Object cast 成 Dog,而是直接使用通型的參數,以簡化程式碼。

到目前為止,透過繼承 Comparable,賦予了一個類別擁有跟其他同樣類別比較的能力,然而,比較的方式只限於一種。在上述的例子中為 size,做為比較的標準,當我們想透過不同的因子來比較時,就需要另一個新的介面 Comparator。

想像 Comparator 為一個第三方單位,只擁有一個方法 compare,比較兩個類別的大小。和 compareTo 類似,都是以回傳整數等於、大於、小於 0 的方式判斷等於、大於,或是小於。

實作的方法會在類別內新增一個巢狀類別並繼承介面 Comparator。舉例來說,假如不想再用 size 來比較 Dog 的大小,而是想用 Dog 的 name 以字母排序的方式比較,會先建立一個巢狀類別 NameComparator,如下所示。在真實的應用會先將該巢狀類別先以 private 宣告,再建立另一個函數 getNameComparator 回傳此 NameComparator。如果想要用其他方式比較,例如 size 等等,只需要繼續建立新的巢狀類別繼承 Comparator 即可。

接著只要呼叫函數即可使用 Comparator 的方法 compare 來比較兩個物件。

Comparator<Dog> nc = Dog.getNameComparator();

從 Comparable 和 Comparator 的範例中可以發現,雖然可以單純新增方法就可以賦予類別比較的能力,不過透過介面繼承,可以讓 Maximizer.max() 的參數推廣到任意繼承介面的類別,換句話說,類別的型態不只是類別本身,還包含了從父類別繼承下來的類別。

--

--