深入探討TypeScript中implements與extends的區(qū)別
在 TypeScript 中,我們經(jīng)常會(huì)遇到兩個(gè)關(guān)鍵字,即 implements 和 extends。雖然它們?cè)诖a中看起來(lái)相似,但它們實(shí)際上有著不同的作用和用法。本文將深入探討這兩個(gè)關(guān)鍵字之間的區(qū)別,幫助讀者更好地理解它們?cè)?TypeScript 中的應(yīng)用。
class和interface的區(qū)別
要理解extends和implements的區(qū)別,得對(duì)類和接口的概念熟稔于心,它們?cè)谡Z(yǔ)法和用途上的關(guān)鍵區(qū)別。
記住:
- 類是創(chuàng)建對(duì)象的模板,支持封裝、繼承和多態(tài)。
- 接口是描述對(duì)象形狀的抽象結(jié)構(gòu),用于確保對(duì)象符合特定的規(guī)范。
類
類是一種具有屬性和方法的藍(lán)圖,它用于創(chuàng)建對(duì)象。通過(guò)類,可以實(shí)例化對(duì)象,讓多個(gè)對(duì)象共享相同的結(jié)構(gòu)和行為。類支持封裝、繼承和多態(tài)等面向?qū)ο蟮奶匦浴?/p>
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log(`Hello, my name is ${this.name}.`);
}
}
const person = new Person("John");
person.sayHello();
用途:
- 用于創(chuàng)建對(duì)象和實(shí)現(xiàn)面向?qū)ο蟮木幊谭妒健?/li>
- 支持封裝,可以隱藏對(duì)象的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。
- 支持繼承,通過(guò)繼承可以重用已有的類的屬性和方法。
- 支持多態(tài),可以使用相同的接口調(diào)用不同的實(shí)現(xiàn)。
在typescript中類時(shí)有抽象類和非抽象類之分的,是面向?qū)ο缶幊讨袃煞N不同的類的概念,主要區(qū)別在于類的設(shè)計(jì)和用途。
抽象類
抽象類是一種不能被實(shí)例化的類,用于定義對(duì)象的通用結(jié)構(gòu)和行為。抽象類可以包含抽象方法(沒(méi)有具體實(shí)現(xiàn)的方法)和具體方法(有實(shí)際實(shí)現(xiàn)的方法)。
特點(diǎn):
- 抽象類不能被直接實(shí)例化,即不能使用 new 關(guān)鍵字創(chuàng)建抽象類的實(shí)例。抽象類主要用于作為其他類的基類,提供通用的屬性和方法的框架,但不能獨(dú)立存在。
- 抽象類中可以包含抽象方法,這些方法沒(méi)有具體的實(shí)現(xiàn),而是要求子類必須提供具體的實(shí)現(xiàn)。抽象方法使用 abstract 關(guān)鍵字標(biāo)記。
- 抽象類可以被其他類繼承,子類必須實(shí)現(xiàn)抽象類中的所有抽象方法,否則子類也必須聲明為抽象類。
abstract class Shape {
abstract draw(): void; // 抽象方法
area() {
console.log("Calculating area...");
}
}
class Circle extends Shape {
draw() {
console.log("Drawing a circle");
}
}
非抽象類
非抽象類是可以被實(shí)例化的類,它可以包含屬性、方法,以及具體的實(shí)現(xiàn)。非抽象類通常用于創(chuàng)建可以直接實(shí)例化和使用的對(duì)象。
特點(diǎn):
- 可以被實(shí)例化:非抽象類可以被直接實(shí)例化,使用 new 關(guān)鍵字可以創(chuàng)建非抽象類的對(duì)象。
- 具體方法:非抽象類中可以包含具體的方法實(shí)現(xiàn),而不僅限于抽象方法。這些方法可以被繼承或直接調(diào)用。
- 繼承:非抽象類可以被其他類繼承,子類可以選擇性地覆蓋或擴(kuò)展父類中的方法。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound() {
console.log("Some generic sound");
}
}
class Dog extends Animal {
makeSound() {
console.log("Woof! Woof!");
}
}
抽象類和非抽象類的區(qū)別
抽象類:
- 不能被直接實(shí)例化。
- 可以包含抽象方法和具體方法。
- 用于提供類的基本框架,強(qiáng)制子類實(shí)現(xiàn)特定行為。
非抽象類:
- 可以被直接實(shí)例化。
- 可以包含屬性、方法,以及具體的實(shí)現(xiàn)。
- 子類可以選擇性地覆蓋或擴(kuò)展父類的方法。
接口
接口是一種用于描述對(duì)象形狀(即屬性和方法)的抽象結(jié)構(gòu)。接口并沒(méi)有提供具體的實(shí)現(xiàn),而是定義了一組規(guī)范,用于確保符合該接口的對(duì)象具有特定的結(jié)構(gòu)。
interface Person {
name: string;
sayHello(): void;
}
const person: Person = {
name: "John",
sayHello() {
console.log(`Hello, my name is ${this.name}.`);
}
};
person.sayHello();
用途:
- 用于描述對(duì)象的形狀,確保對(duì)象具有特定的結(jié)構(gòu)。
- 支持通過(guò)接口來(lái)定義類的契約,使得類必須實(shí)現(xiàn)特定的屬性和方法。
- 支持通過(guò)接口來(lái)組合多個(gè)對(duì)象的結(jié)構(gòu),從而實(shí)現(xiàn)靈活的代碼組織。
extends關(guān)鍵字
extends 關(guān)鍵字主要用于創(chuàng)建類或接口的繼承關(guān)系。通過(guò) extends,一個(gè)類或接口可以繼承另一個(gè)類或接口的屬性和方法,從而可以重用已有的代碼并擴(kuò)展其功能。不可以重寫(xiě)屬性,但是可以重寫(xiě)方法。
類繼承類
非抽象類繼承非抽象類
非抽象類繼承非抽象類即簡(jiǎn)單的屬性和方法的繼承。
當(dāng)一個(gè)非抽象類繼承另一個(gè)非抽象類時(shí),子類會(huì)繼承父類的屬性和方法,并且子類可以選擇性地覆蓋或擴(kuò)展父類的方法。
// 父類
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound() {
console.log("Some generic sound");
}
}
// 子類繼承父類
class Dog extends Animal {
// 可以覆蓋父類的方法
makeSound() {
console.log("Woof! Woof!");
}
// 可以擴(kuò)展父類的方法
greetOwner() {
console.log(`Hello, owner! My name is ${this.name}.`);
}
}
非抽象類繼承抽象類
當(dāng)一個(gè)非抽象類繼承抽象類時(shí),子類必須提供抽象類中定義的抽象方法的具體實(shí)現(xiàn)。抽象類中的抽象方法是沒(méi)有具體實(shí)現(xiàn)的方法,而是由子類去實(shí)現(xiàn)。這強(qiáng)制了子類必須遵循抽象類中定義的一些行為規(guī)范。
// 抽象類
abstract class Shape {
abstract draw(): void; // 抽象方法
area() {
console.log("Calculating area...");
}
}
// 非抽象類繼承抽象類
class Circle extends Shape {
radius: number;
constructor(radius: number) {
super(); // 調(diào)用父類的構(gòu)造函數(shù)
this.radius = radius;
}
// 實(shí)現(xiàn)抽象方法
draw() {
console.log("Drawing a circle");
}
// 可以擴(kuò)展父類的方法
calculateArea() {
const area = Math.PI * this.radius ** 2;
console.log(`Area: ${area}`);
}
}
抽象類繼承抽象類
抽象類可以繼承非抽象類的屬性和方法,而且它也可以包含抽象方法,這些抽象方法需要在子類中進(jìn)行實(shí)現(xiàn)。
// 非抽象類
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound() {
console.log("Some generic sound");
}
}
// 抽象類繼承非抽象類
abstract class Dog extends Animal {
abstract wagTail(): void; // 抽象方法
bark() {
console.log("Woof! Woof!");
}
}
// 具體的子類實(shí)現(xiàn)抽象類中的抽象方法
class Labrador extends Dog {
wagTail() {
console.log("Tail wagging...");
}
}
抽象類繼承抽象類
抽象類是可以繼承抽象類的。這種繼承關(guān)系允許在類的層次結(jié)構(gòu)中建立一種更高層次的抽象,并要求子類實(shí)現(xiàn)更具體的行為。
// 抽象類
abstract class Animal {
abstract makeSound(): void; // 抽象方法
move() {
console.log("Animal is moving...");
}
}
// 另一個(gè)抽象類繼承抽象類
abstract class Bird extends Animal {
abstract fly(): void; // 抽象方法
chirp() {
console.log("Chirp chirp!");
}
}
// 具體的子類實(shí)現(xiàn)抽象類中的抽象方法
class Sparrow extends Bird {
makeSound() {
console.log("Sparrow is making sound");
}
fly() {
console.log("Sparrow is flying");
}
}
類繼承接口
類是無(wú)法直接使用extends繼承接口的,只能使用implements去實(shí)現(xiàn)接口。
接口繼承類
接口是不能直接繼承類的。
類可以作為接口的一部分,從而實(shí)現(xiàn)接口繼承類。這意味著你可以定義一個(gè)接口,它包含了類的實(shí)例部分的成員,然后其他類可以實(shí)現(xiàn)這個(gè)接口。
這樣的話,實(shí)際上是在強(qiáng)調(diào)類的實(shí)例結(jié)構(gòu)。
abstract class AbstractParent {
abstract abstractFunc():string
}
// 接口繼承類
interface IExample extends AbstractParent{
name:string
age:number
}
需要注意的是,這種方式強(qiáng)調(diào)的是類的實(shí)例結(jié)構(gòu),而不是類的構(gòu)造函數(shù)或靜態(tài)部分。因此,只有類的實(shí)例部分的成員才會(huì)被包含在接口中。
接口繼承接口
接口可以通過(guò) extends 關(guān)鍵字來(lái)繼承其他接口,從而形成接口繼承接口的關(guān)系。
// 定義一個(gè)基礎(chǔ)接口
interface Shape {
color: string;
}
// 定義繼承自 Shape 接口的新接口
interface Square extends Shape {
sideLength: number;
}
// 使用新接口
let square: Square = {
color: "red",
sideLength: 10,
};
小結(jié)
- 非抽象類繼承非抽象類:
簡(jiǎn)單的屬性和方法的繼承。
子類繼承父類的屬性和方法,可以選擇性地覆蓋或擴(kuò)展父類的方法。
- 非抽象類繼承抽象類:
- 子類需要實(shí)現(xiàn)抽象類中定義的抽象方法。
- 對(duì)于屬性,非抽象類可以直接繼承,無(wú)需單獨(dú)定義。
- 抽象類繼承非抽象類:
- 簡(jiǎn)單的屬性和方法的繼承。
- 子類繼承父類的屬性和方法。
- 抽象類繼承抽象類:
- 子類繼承父類的屬性和方法。
- 子類必須提供抽象類中定義的抽象方法的具體實(shí)現(xiàn)。
- 類繼承接口:
- 類無(wú)法直接使用 extends 繼承接口,只能使用 implements 去實(shí)現(xiàn)接口。
- 接口繼承類:
- 接口不能直接繼承類。
- 類可以作為接口的一部分,實(shí)現(xiàn)接口繼承類,強(qiáng)調(diào)類的實(shí)例結(jié)構(gòu)。
- 接口繼承接口:
- 簡(jiǎn)單的屬性和方法的繼承。
- 接口可以通過(guò) extends 關(guān)鍵字繼承其他接口。
implements關(guān)鍵字
implements 關(guān)鍵字來(lái)確保一個(gè)類實(shí)現(xiàn)了特定的接口。接口定義了一組成員(方法、屬性等),而通過(guò) implements,你可以告訴 TypeScript 編譯器,這個(gè)類將提供接口中指定的所有成員。
類實(shí)現(xiàn)類
非抽象類實(shí)現(xiàn)非抽象類
實(shí)現(xiàn)類里面所有的屬性和方法都要在目標(biāo)類里重新定義和實(shí)現(xiàn)。
// 定義一個(gè)非抽象類
class MyBaseClass {
baseMethod(): void {
console.log("Base class method");
}
}
// 定義另一個(gè)非抽象類并實(shí)現(xiàn) MyBaseClass
class MyDerivedClass implements MyBaseClass {
baseMethod(): void {
console.log("Derived class method");
}
derivedMethod(): void {
console.log("Derived class-specific method");
}
}
非抽象類實(shí)現(xiàn)抽象類
抽象類里的所有屬性和方法都需要在非抽象類里定義和實(shí)現(xiàn)。
// 定義一個(gè)抽象類
abstract class MyAbstractClass {
abstract abstractMethod(): void;
}
// 定義一個(gè)非抽象類并實(shí)現(xiàn)抽象類
class MyNonAbstractClass implements MyAbstractClass {
abstractMethod(): void {
console.log("Implementation of abstractMethod");
}
nonAbstractMethod(): void {
console.log("Non-abstract class-specific method");
}
}
抽象類實(shí)現(xiàn)抽象類
實(shí)現(xiàn)類里面所有的屬性都要在目標(biāo)類里重新定義,所有方法需要被實(shí)現(xiàn)或者使用 abstract 定義為抽象方法。
// 定義一個(gè)抽象類
abstract class BaseAbstractClass {
abstract abstractMethod(): void;
abstract abstractProperty: string;
}
// 另一個(gè)抽象類繼承自 BaseAbstractClass
abstract class DerivedAbstractClass extends BaseAbstractClass {
// 重新定義屬性
abstract abstractProperty: string;
concreteProperty: number = 42;
// 實(shí)現(xiàn)抽象方法
abstractMethod(): void {
console.log("Implementation of abstractMethod in DerivedAbstractClass");
}
// 新的抽象方法
abstract newAbstractMethod(): void;
}
// 具體的實(shí)現(xiàn)類
class ConcreteClass extends DerivedAbstractClass {
// 重新定義屬性
abstractProperty: string = "Concrete Property";
// 實(shí)現(xiàn)新的抽象方法
newAbstractMethod(): void {
console.log("Implementation of newAbstractMethod in ConcreteClass");
}
// 新的具體方法
concreteMethod(): void {
console.log("Concrete method in ConcreteClass");
}
}
抽象類實(shí)現(xiàn)非抽象類
非抽象類里面所有的屬性都要在抽象類里重新定義,所有方法需要被實(shí)現(xiàn)或者使用 abstract 定義為抽象方法。
// 定義一個(gè)非抽象類
class MyBaseClass {
nonAbstractProperty: string = "Non-abstract Property";
nonAbstractMethod(): void {
console.log("Non-abstract method in MyBaseClass");
}
}
// 抽象類繼承自非抽象類
abstract class MyAbstractClass extends MyBaseClass {
// 重新定義屬性
abstract nonAbstractProperty: string;
// 實(shí)現(xiàn)非抽象方法
nonAbstractMethod(): void {
console.log("Implementation of nonAbstractMethod in MyAbstractClass");
}
// 新的抽象方法
abstract newAbstractMethod(): void;
}
// 具體的實(shí)現(xiàn)類
class ConcreteClass extends MyAbstractClass {
// 重新定義屬性
nonAbstractProperty: string = "Concrete Property";
// 實(shí)現(xiàn)新的抽象方法
newAbstractMethod(): void {
console.log("Implementation of newAbstractMethod in ConcreteClass");
}
// 新的具體方法
concreteMethod(): void {
console.log("Concrete method in ConcreteClass");
}
}
類實(shí)現(xiàn)接口
抽象類實(shí)現(xiàn)接口
接口所有屬性都要重新定義,接口所有方法需要實(shí)現(xiàn)或者使用 abstract 定義為抽象方法。
// 定義一個(gè)接口
interface MyInterface {
interfaceProperty: string;
interfaceMethod(): void;
}
// 抽象類實(shí)現(xiàn)接口
abstract class MyAbstractClass implements MyInterface {
// 重新定義接口中的屬性
interfaceProperty: string;
// 實(shí)現(xiàn)接口中的方法
interfaceMethod(): void {
console.log("Implementation of interfaceMethod in MyAbstractClass");
}
// 新的抽象方法
abstract newAbstractMethod(): void;
}
// 具體的實(shí)現(xiàn)類
class ConcreteClass extends MyAbstractClass {
// 重新定義屬性
interfaceProperty: string = "Concrete Property";
// 實(shí)現(xiàn)新的抽象方法
newAbstractMethod(): void {
console.log("Implementation of newAbstractMethod in ConcreteClass");
}
// 新的具體方法
concreteMethod(): void {
console.log("Concrete method in ConcreteClass");
}
}
非抽象類實(shí)現(xiàn)接口
接口所有屬性都要重新定義,接口所有方法需要實(shí)現(xiàn)。
// 定義一個(gè)接口
interface MyInterface {
interfaceProperty: string;
interfaceMethod(): void;
}
// 非抽象類實(shí)現(xiàn)接口
class MyClass implements MyInterface {
// 重新定義接口中的屬性
interfaceProperty: string = "Default Property";
// 實(shí)現(xiàn)接口中的方法
interfaceMethod(): void {
console.log("Implementation of interfaceMethod in MyClass");
}
// 新的具體方法
myClassMethod(): void {
console.log("Method specific to MyClass");
}
}
接口不能實(shí)現(xiàn)接口
接口不能實(shí)現(xiàn)類
小結(jié)
- 類實(shí)現(xiàn)類
非抽象類實(shí)現(xiàn)非抽象類:實(shí)現(xiàn)類里面所有的屬性和方法都要在目標(biāo)類里重新定義和實(shí)現(xiàn)
非抽象類實(shí)現(xiàn)抽象類:抽象類里的所有屬性和方法都需要在非抽象類里定義和實(shí)現(xiàn)
抽象類實(shí)現(xiàn)抽象類:實(shí)現(xiàn)類里面所有的屬性都要在目標(biāo)類里重新定義,所有方法需要被實(shí)現(xiàn)或者使用 abstract 定義為抽象方法
抽象類實(shí)現(xiàn)非抽象類:非抽象類里面所有的屬性都要在抽象類里重新定義,所有方法需要被實(shí)現(xiàn)或者使用 abstract 定義為抽象方法
- 類實(shí)現(xiàn)接口
- 抽象類實(shí)現(xiàn)接口:接口所有屬性都要重新定義,接口所有方法需要實(shí)現(xiàn)或者使用 abstract 定義為抽象方法
- 非抽象類實(shí)現(xiàn)接口:接口所有屬性都要重新定義,接口所有方法需要實(shí)現(xiàn)
- 接口實(shí)現(xiàn)接口:接口不能實(shí)現(xiàn)接口
- 接口實(shí)現(xiàn)類:接口不能實(shí)現(xiàn)類
extends和implements的區(qū)別
通過(guò)前文的介紹,相信你已經(jīng)能夠清晰地區(qū)分這兩者了。在本節(jié)中,我將對(duì)以上內(nèi)容進(jìn)行總結(jié)。首先,我們列舉它們的共同點(diǎn):
- 都可以建立類與類之間的關(guān)聯(lián)。
- 針對(duì)抽象類中的抽象方法,都必須要進(jìn)行實(shí)現(xiàn)。
接下來(lái),我們?cè)敿?xì)列舉它們的不同之處:
- extends能夠?qū)崿F(xiàn)接口與接口、接口與類的繼承;而implements不能實(shí)現(xiàn)接口與接口、接口與類的實(shí)現(xiàn)。
- implements可以實(shí)現(xiàn)類實(shí)現(xiàn)接口,而extends不能實(shí)現(xiàn)類繼承接口。
- 在使用implements時(shí),必須定義或?qū)崿F(xiàn)所有屬性和方法;而extends只需重新定義或?qū)崿F(xiàn)方法,對(duì)于屬性來(lái)說(shuō),可以直接繼承,無(wú)需單獨(dú)定義。
總結(jié)
extends 用于建立類與類之間的繼承關(guān)系,而 implements 用于確保類實(shí)現(xiàn)了特定的接口。 extends 在類的層次結(jié)構(gòu)中用于繼承,而 implements 則用于實(shí)現(xiàn)接口的規(guī)范。