網站搜尋

在 TypeScript 中,'extends' 和 'implements' 之間有什麼區別


我想知道人類與孩子有什麼共同點,以及它們有何不同。

class Person {
  name: string;
  age: number;
}
class Child extends Person {}
class Man implements Person {}

答案#1

簡短版本

  • extends 的意思是:

新類別是一個子類別。它獲得繼承所帶來的好處。它擁有父類別的所有屬性和方法。它可以覆寫其中的一些並實現新的方法,但父類別的內容已經包含在內。

  • implements 的意思是:

這個 新類別 可以被視為 相同的 「形狀」,但 它不是子類別。它可以傳遞給任何需要 Person 的方法,不論它的父類別是否與 Person 不同。

更多 ...

在物件導向程式設計(像是 C# 或 Java 這類語言)中,我們會使用

extends 用於從繼承中獲益。

... 在大多數基於類別的物件導向語言中,繼承是一種機制,透過該機制,一個物件可以繼承父物件的所有屬性和行為。繼承允許程式設計師:建立基於現有類別的類別 ...

implements 將更多用於多態性。

... 多型是為不同類型的實體提供單一介面...

所以我們可以為我們的類別 Man 擁有一個完全不同的繼承樹:

class Man extends Human ...

但如果我們也宣告 Man 可以假裝成 Person 類型:

class Man extends Human 
          implements Person ...

...那麼我們可以在任何需要 Person 的地方使用它。我們只需要實現 Person 的「介面」(也就是實作它所有的公開內容)。

實作其他類別?那真是很酷的東西

Javascript 的優點之一是內建支援 duck typing。

「如果它走路像鴨子,叫聲也像鴨子,那它一定是鴨子。」

所以,在 Javascript 中,如果兩個不同的物件有一個相似的方法(例如 render()),它們可以被傳遞給期望該方法的函數:

function(engine){
  engine.render() // any type implementing render() can be passed
}

為了在 Typescript 中不失去這一點,我們可以用更多類型支持的方式做到同樣的事情。而這就是

class implements class

在適當的地方,它有其作用。

在物件導向程式語言如 C# 中,沒有辦法做到那個。

文件應該可以在這裡提供幫助:

介面擴充類別

當一個介面類型擴展一個類別類型時,它會繼承該類別的成員,但不繼承它們的實作。這就像介面聲明了該類別的所有成員,但沒有提供實作。介面甚至會繼承基類的私有和受保護成員。這意味著,當您創建一個擴展具有私有或受保護成員的類別的介面時,該介面類型只能由該類別或其子類別實作。

當你有一個大型繼承層次結構時,這很有用,但你想要指定你的代碼僅適用於具有某些屬性的子類。子類除了繼承自基類之外,不必有其他關聯。例如:

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
    private state: any;
    select() { }
}

class Location {

}

所以,當

  • extends 的意思是它會從其父類別繼承所有東西
  • 在這種情況下,implements 幾乎就像是在實現一個介面。子物件可以假裝它是其父物件……但它不會獲得任何實作。

答案#2

你在 TypeScript(以及其他一些面向對象的語言)中有類別和介面。

介面沒有實作;它只是這種類型擁有哪些成員/方法的「契約」。

例如:

interface Point {
    x: number;
    y: number;
    distance(other: Point): number;
}

實作這個 Point 介面的實例必須有兩個型別為 number 的成員:x 和 y,以及一個方法 distance,該方法接收另一個 Point 實例並返回一個 number。

該介面沒有實作其中任何一個。

類別是實作:

class PointImplementation implements Point {
    public x: number;
    public y: number;
    
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    
    public distance(other: Point): number {
        return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2));
    }
}

(在遊樂場中的程式碼)

在你的例子中,你在擴展 Person 類時把它當作類來對待,而在實現它時又把它當作介面來對待。

您的代碼:

class Person {
    name: string;
    age: number;
}

class Child  extends Person {}

class Man implements Person {}

它有以下編譯錯誤:

類別 'Man' 錯誤地實作了介面 'Person'。型別 'Man' 缺少屬性 'name'。

那是因為介面缺乏實作。
所以如果你 實作 一個類別,你只會取得它的「契約」而沒有實作,因此你需要這麼做:

class NoErrorMan implements Person {
    name: string;
    age: number;
}

(在遊樂場中的程式碼)

最重要的是,在大多數情況下,你會想要繼承另一個類,而不是實作它。

答案#3

extends 與 implements

  • extends:被擴展的子類將 繼承 它所 extends 的類的所有屬性和方法。
  • implements:使用 implements 關鍵字的類別需要 實作 它所 implements 的類別的所有屬性和方法。

簡單來說:

  • extends:你可以從父類別獲得所有這些方法/屬性,因此你不必自己實現它們。
  • implements:它是一個類別必須遵守的契約。該類別至少必須 實作 以下方法/屬性。

範例:

class Person {
  name: string;
  age: number;

  walk(): void {
    console.log('Walking (person Class)')
  }

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
class child extends Person { }

// Man has to implement at least all the properties
// and methods of the Person class
class man implements Person {
  name: string;
  age: number

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  walk(): void {
    console.log('Walking (man class)')
  }

}
    
(new child('Mike', 12)).walk();
// logs: Walking(person Class)

(new man('Tom', 12)).walk();
// logs: Walking(man class)

在這個例子中,我們可以觀察到子類別繼承了 Person 的所有內容,而 man 類別必須自己實現 Person 的所有內容。

如果我們從 man 類別中移除某個東西,例如 walk 方法,我們將會得到以下編譯時錯誤:

類別 'man' 錯誤地實作了類別 'Person'。您是否想擴展 'Person' 並作為子類別繼承其成員?類別 'man' 中缺少屬性 'walk',而類別 'Person' 需要此屬性。(2720)