繼承是 Object-Oriented Programming 當中的一個概念。如果我們說一個類別 B 繼承了類別 A,則 B稱為 A的子類別,A 就是 B的父類別。繼承是為了讓子類別可以擁有父類別中的屬性和方法,不需要再重複去撰寫。然而子類別也可以重新定義並重寫這些屬性跟方法某些方法。而子類別通常會再擴充新的屬性和方法。 那就讓我們來看看這四個語言如何實作這個概念吧!
class Person:
def __init__(self, first_name, last_name, id_number):
self.first_name = first_name
self.last_name = last_name
self.id_number = id_number
def print_person(self):
print("Name:", self.last_name + ",", self.first_name)
print("ID:", self.id_number)
class Student(Person):
def __init__(self, first_name, last_name, id_number, test_scores):
super().__init__(first_name, last_name, id_number)
self.test_scores = test_scores
def calculate(self):
total = 0
for test_score in self.test_scores:
total += test_score
avg = total / len(self.test_scores)
if 90 <= avg <= 100:
return 'O'
if 80 <= avg < 90:
return 'E'
if 70 <= avg < 80:
return 'A'
if 55 <= avg < 70:
return 'P'
if 40 <= avg < 55:
return 'D'
return 'T'
line = input().split()
first_name = line[0]
last_name = line[1]
id_num = line[2]
num_scores = int(input())
scores = list(map(int, input().split()))
s = Student(first_name, last_name, id_num, scores)
s.print_person()
print("Grade:", s.calculate())
object
這個類別。Person
,其中他在 Constructor 中初始化了三個屬性 first_name
, last_name
以及 id_number
,並且有一個 Method print_person
。Student
則是 Person
的子類別,我們可以看到在宣告 class Student
時,用 class Student(Person)
來表示繼承關係。接著看到 Student
的 __init__
中有一行是 super().__init__(first_name, last_name, id_number)
,這裡 super()
表示的是父類別,所以 super().__init__(first_name, last_name, id_number)
是指引用父類別中的 __init__
來協助初始化。另外 Student
也自己定義了calculate
這個 Method。假使你想知道某個類別的父類別是什麼,可以用 __bases__
這個屬性看出來。例如我們印出 Student.__bases__
就會看到(<class ‘__main__.Person’>,)
,因為是個可修改的屬性,這也表示我們可以動態去修改某個子類別的父類別囉!class A(B, C)
。這時如果 B
跟 C
有相同名稱的屬性跟方法時,會以 B
的為優先。當我們在多重繼承下去使用 super
的時候就會有點超乎想像的複雜了,先前我們說super()
表示的是父類別,這是在單一繼承的時候是這樣,但是如果今天是多重繼承的時候,會發現 super()
其實和父類別沒有實質的關連,有興趣的可以參考這裡囉!class Person(val firstName: String, val lastName: String, val idNum: String) {
def printPerson() {
println("First Name: " + firstName)
println("Last Name: " + lastName)
println("ID: " + idNum)
}
}
class Student(override val firstName: String,
override val lastName: String,
override val idNum: String,
test_scores: List[Int]) extends Person(firstName, lastName, idNum) {
def calculate(): String = {
val avg = test_scores.sum / test_scores.length
avg match {
case x if x < 40 => "D"
case x if x >= 40 && x < 60 => "B"
case x if x >= 60 && x < 75 => "A"
case x if x >= 75 && x < 90 => "E"
case _ => "O"
}
}
}
object Solution {
def main(args: Array[String]) {
val student = new Student("aa", "bb", "123", List(70, 90))
student.printPerson()
println("Grade: " + student.calculate)
}
}
Person
這個 class,其中 constructor 分別定義了 firstName
, lastName
, idNum
等等屬性。而 Person
也定義了 printPerson
Method。Student
繼承了 Person
,在這裡看到如果要繼承的話,在 Scala 用的是extends
這個關鍵字,像是這裡的extends Person(firstName, lastName, idNum)
。由於 Person
本身在建構的時候就要給予 firstName
,lastName
,idNum
這三個屬性的值,所以我們必須在建構 Student
時,去提供這三個父類別屬性的值,也就是 override val firstName: String
, override val lastName: String
, override val idNum: String
(這裡用 override
來表示要覆蓋父類別中同名的屬性或方法)。在這裡當子類別的 instance 被產生時,也會跑父類別的 constructor 囉!可以在父類別中例如隨便印一行字,就會發現當子類別產生新的 instance 時,這行字也會被印出來囉!如果要在子類別中使用父類別中的方法,就可以用 super.<Method>
來引用囉!scala.AnyRef
這個 Class。在這裡我們可以發現突然在講 Golang 和 Rust 的時候一下就講完了,因為在這兩個語言並沒有繼承的概念,雖然可以用其他方式去做出這樣的效果,但因為這兩個語言鼓勵組合而非繼承。如果想要多瞭解這者的優缺點,可以參考這裡 囉!