|
|
在類別中可使用type關鍵字來宣告型態成員,其應用之一,就是為某些形態取別名,例如:type Text = String 在上例中,Text成了String的別名,之後你可以使用Text這個名稱來代替String進行宣告。會使用type來為型態取別名,通常是為了用個更簡潔的名稱。例如,在未匯入scala.collection.mutable.Set時,你程式中所使用的Set名稱所參考的物件,其實是定義在scala.Predef中: package scala object Predef { ... type Map[A, B] = collection.immutable.Map[A, B] type Set[A] = collection.immutable.Set[A] val Map = collection.immutable.Map val Set = collection.immutable.Set ... } val宣告了Map與Set分別參考至collection.immutable.Set與collection.immutable.Map物件,而type為collection.immutable.Set[A]與collection.immutable.Map[A, B]建立了別名Set[A]與Map[A, B]。 type宣告可以是抽象的,也就是暫不指定別名,例如: trait Something {在實作Something的類別中,再指定上例中T實際為哪個型態的別名: class SomethingImpl extends Something {如果為了程式碼清楚起見,也可以在實作時使用實際型態名稱: class SomethingImpl extends Something {在宣告抽象的type成員時,你可以使用 <: 或 >: 限制實際指定的型態,例如: class Some 如上例,T只能是Some或其子類別,否則就會編譯錯誤,如果使用 >: 的話則相反,只能是Some或其父類別。 以下為可能使用抽象type成員的情境,原先也許你設計了以下的類別: class Food 每個動物都會吃東西,所以你定義了一個抽象類別Animal,規範了eat()方法,你也許這麼實作: class Fish extends Food {由於eat()接受的是Food型態的物件,為了避免動物亂吃東西,你得記得在程式中自行實作比對,看看傳入的食物是不是正確的食物,所以: val cat = new Cat 這樣的作法基本上沒錯,但程式設計人員最好得記得要處理例外的狀況,另一方面,如果程式設計人員沒有記得要在程式中執行比對,那就會發生貓吃草的奇怪行為: class Cat extends Animal {如果這不是你所希望看到的結果,你可以考慮改為以下的設計: abstract class Animal {F是抽象type成員,必須是Food或其子類別。實作Animal時: class Cat extends Animal {就可以指定動物實際上到底要吃什麼食物,不用執行時期比對,現在動物可以吃東西了: val cat = new Cat 如果你亂餵東西,那編譯時期就會回報錯誤: val cat = new Cat 這樣的作法,是將執行時期的型態比對,轉為編譯時期的型態檢查,另一方面,你等於放棄eat()的執行時期多型,選擇了編譯時期多型。例如以下這個執行時期多型你就不能作了: val animal: Animal = new Cat |