繼承是物件導向語言設計的核心支柱。Java 採單一實作繼承,C++ 支援多重繼承,而 Scala 作為融合物件與函數範式的語言,提供了獨特且強大的繼承模型。本文將以 Java 開發者熟悉的視角,深入探討 Scala 的繼承機制、語法差異與進階特性。
以 Person 類別為例,Scala 的主建構子參數直接宣告於類別標籤:
class Person(val firstName: String, val lastName: String, val age: Int) {
override def toString = s"[Person: firstName=$firstName lastName=$lastName age=$age]"
}
val 宣告唯讀欄位(若需可變改用 var)。若基類方法需強制子類別實作,使用 abstract 關鍵字:
abstract class Person(val firstName: String, val lastName: String, val age: Int) {
def doSomething // 無主體,等同 Java 抽象方法
}
Scala 將函數視為值,可直接賦值、傳遞、替換。這使「角色模式」成為策略模式的輕量替代:
class Person(val firstName: String, val lastName: String, val age: Int) {
var doSomething: Person => Unit = (p: Person) => println(s"I'm $p and I don't do anything yet!")
def work() = doSomething(this)
override def toString = s"[Person: firstName=$firstName lastName=$lastName age=$age]"
}
val bindi = new Person("Tabinda", "Khan", 38)
bindi.work() // 輸出預設行為
bindi.doSomething = (p: Person) => println("I edit textbooks")
bindi.work() // 行為動態變更
此舉將繼承層級的靜態多型,轉化為執行期的動態行為組合,大幅減少樣板碼。
Scala 子類別需顯式呼叫父類別主建構子:
class Student(firstName: String, lastName: String, age: Int, val subject: String)
extends Person(firstName, lastName, age) { ... }
注意:子類別不可重複宣告 val firstName 等參數,否則會與父類別存取器衝突。若子類別需額外參數,直接加於自身建構子列表即可。
Scala 不區分「欄位存取」與「無參方法呼叫」語法:
abstract class Person(...) {
def weight: Int // 抽象無參方法
}
class Student(...) extends Person(...) {
def weight: Int = age // 以方法實作
}
class Employee(...) extends Person(...) {
val weight: Int = age * 4 // 以唯讀欄位實作
}
呼叫端均為 person.weight,實作細節對外透明。這比 Java 需統一用 getWeight() 更靈活,且支援私有成員同樣語法。
Java 靠 @Override 註解,Scala 則將 override 納入語言關鍵字:
override def toString = s"[Student: firstName=$firstName lastName=$lastName age=$age subject=$subject]"
若未真正覆寫父類別方法,編譯直接報錯,消除拼字錯誤導致的靜默失效。
同 Java,final 可修飾方法(禁止覆寫)或類別(禁止繼承),亦適用於運算子方法(如 +、-)。
Scala 類別可繼承 Java 類別,反之亦然。Scala 類別最終繼承 scala.Any,分支為 AnyVal(值類型)與 AnyRef(參考類型,繼承 java.lang.Object)。實務上幾乎無感調用:
// Scala 繼承 Java 類別
class ScalaJavaPerson(fn: String, ln: String, age: Int) extends JavaPerson(fn, ln, age) { ... }
// Java 使用 Scala 類別
Student s = StudentFactory.getStudent("Neo", "Anderson");
s.teach("Kung fu");
Scala 編譯器自動實作 ScalaObject 介面並處理 Any 相容性,極少遇到極端情況。
Scala 繼承模型與 Java 高度相似:方法覆寫、成員可見性、建構子鏈皆同源。核心差異在語法簡潔度與函數式建模能力。掌握細微差別後,Java 開發者可無縫編寫 Scala 實作的 JUnit、Servlet、Swing 元件——Scala 團隊提供的 OOPScala 電子表格範例,以數量級更少程式碼實現同等功能。建議從專案的小模組著手嘗試,體驗「更好的 Java」。