iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
0
Software Development

30 天把自己榨好榨滿的四週四語言大挑戰!系列 第 13

[Day 12] 如果我有富爸爸

繼承是 Object-Oriented Programming 當中的一個概念。如果我們說一個類別 B 繼承了類別 A,則 B稱為 A的子類別,A 就是 B的父類別。繼承是為了讓子類別可以擁有父類別中的屬性和方法,不需要再重複去撰寫。然而子類別也可以重新定義並重寫這些屬性跟方法某些方法。而子類別通常會再擴充新的屬性和方法。 那就讓我們來看看這四個語言如何實作這個概念吧!


Python 3

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())
  • 在 Python 的世界,假使 class 沒有表明是繼承什麼類別,其實 default 會繼承 object 這個類別。
    首先我們有個父類別叫做 Person ,其中他在 Constructor 中初始化了三個屬性 first_name , last_name 以及 id_number ,並且有一個 Method print_person
  • class 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’>,) ,因為是個可修改的屬性,這也表示我們可以動態去修改某個子類別的父類別囉!
  • 最後來談談 Python 其實是可以多重繼承的,只要在宣告 class 的時候,在括號內引入多個父類別就可以了,例如 class A(B, C) 。這時如果 BC 有相同名稱的屬性跟方法時,會以 B 的為優先。當我們在多重繼承下去使用 super 的時候就會有點超乎想像的複雜了,先前我們說super()表示的是父類別,這是在單一繼承的時候是這樣,但是如果今天是多重繼承的時候,會發現 super() 其實和父類別沒有實質的關連,有興趣的可以參考這裡囉!

Scala

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。
  • 而 class 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 中有沒有多重繼承呢?答案是沒有,要達到這樣的效果,在 Scala 有像是 interface 的 Trait,但 Trait 中的屬性和方法是可以有實作的,不像 interface 只是定義屬性名跟 Signature。同一個 class 可以有多個 Trait,來達到多重繼承的效果。不過 Trait 跟父類別在使用概念上還是有不同之處,這部分就等到我們在講 interface 的時候再說明囉!
  • 在 Scala 假設你的 Class 沒有顯性表明繼承其他 Class 的話,那麼預設會繼承 scala.AnyRef 這個 Class。

Golang

  • Golang 本身沒有繼承的關鍵字,也沒有 Class 的概念,雖然可以用 Struct 和一些技巧去達到繼承的效果,但是在 Golang 的世界,是提倡所謂的“組合” (has a),而非“繼承” (is a)。組合的概念是把東西放在一起,例如車子就是把車輪、引擎、椅子等等東西放在一起變成了車子。有興趣的可以參考這篇囉!而在 Golang 要實現多態的話,是藉由實作 interface,這部分當我們在講 interface 的時候再來深入探討探討!

Rust

  • Rust 和 Golang 一樣都沒有繼承的概念,而是鼓勵組合大於繼承。在 Rust 中是用 Trait (類似 Golang 中的 interface) 來做為多態的基礎,所以也是等講到 interface 的時候再來深談囉!

結語

在這裡我們可以發現突然在講 Golang 和 Rust 的時候一下就講完了,因為在這兩個語言並沒有繼承的概念,雖然可以用其他方式去做出這樣的效果,但因為這兩個語言鼓勵組合而非繼承。如果想要多瞭解這者的優缺點,可以參考這裡 囉!


上一篇
[Day 11] 我的世界變多維了!
下一篇
[Day 13] 談談抽象這件事
系列文
30 天把自己榨好榨滿的四週四語言大挑戰!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言