class
앞에 abstract
라고 표기합니다. 또한 추상 메서드를 정의할 때도 abstract
라고 표기합니다.abstract
키워드를 통해 추상 메서드가 없는 추상 클래스를 만들 수도 있습니다. (어떤 분이 설명이 하시길 추상 클래스는 "객체를 설계할 때, 추상화 작업, 즉 객체들의 공통 부분을 추출하는 부분에서 다형성을 구현할 수 있는 방법 중 하나"라고 설명하셨습니다.)1abstract class Animal {
2 name: string;
3
4 constructor(name: string) {
5 this.name = name;
6 }
7
8 // 추상 메서드
9 abstract makeSound(): void;
10 // 일반 메서드
11 move(): void {
12 console.log("roaming the earth...");
13 }
14}
abstract
키워드를 사용합니다. 추상 클래스 자체를 인스턴스화 할 수 없는 이유는 추상 메서드가 구현되지 않았기 때문입니다.1class Dog extends Animal {
2 // 메서드 재정의
3 move() {
4 console.log("running...");
5 }
6
7 // 추상 메서드 구현
8 makeSound() {
9 console.log("bow wow");
10 }
11}
12
13class Cat extends Animal {}
14// Error: Non-abstract class 'Cat' does not implement inherited abstract member 'makeSound' from class 'Animal'.
interface
키워드를 사용하여 정의합니다. 인터페이스는 상호 간에 정의한 약속 혹은 규칙입니다. Typescript에서 인터페이스는 다음과 같은 범주에 대해 약속을 정의할 수 있습니다.1interface Animal {
2 name: string;
3 makeSound(): void;
4 move(): void;
5}
implements
키워드를 사용하여 클래스를 구현할 수 있습니다.1class Dog implements Animal {
2 name: string;
3
4 constructor(name: string) {
5 this.name = name;
6 }
7
8 makeSound() {
9 console.log("bow wow");
10 }
11
12 move() {
13 console.log("running...");
14 }
15}
extends
)와 구현(implements
)1abstract class Animal {
2 name: string;
3
4 constructor(name: string) {
5 this.name = name;
6 }
7
8 abstract makeSound(): void;
9
10 move(): void {
11 console.log("roaming the earth...");
12 }
13}
14
15class Dog extends Animal {
16 constructor(name: string) {
17 super(name);
18 }
19
20 // 추상 메서드 구현
21 makeSound() {
22 console.log("bow wow");
23 }
24
25 // 상위 클래스의 메서드를 재정의
26 move() {
27 // 상위 클래스의 메서드 호출
28 super.move();
29 console.log("running...");
30 }
31}
implements
)하면 해당 클래스는 인터페이스에 정의된 모든 메서드를 구현해야 합니다.1interface Animal {
2 name: string;
3 makeSound(): void;
4 move(): void;
5}
6
7class Dog implements Animal {
8 name: string;
9
10 constructor(name: string) {
11 this.name = name;
12 }
13
14 makeSound() {
15 console.log("bow wow");
16 }
17
18 move() {
19 console.log("running...");
20 }
21}
22
23class Cat implements Animal {
24 name: string;
25
26 constructor(name: string) {
27 this.name = name;
28 }
29
30 makeSound() {
31 console.log("meow");
32 }
33}
34// Error: Class 'Cat' incorrectly implements interface 'Animal'.
extends
키워드를 사용하여 상속받을 수 있습니다. 추상 클래스는 단일 상속만 지원합니다.1class Animal {
2 makeSound() {
3 console.log("roaming the earth...");
4 }
5}
6
7class Dog extends Animal {
8 makeSound() {
9 console.log("bow wow");
10 }
11}
12
13class Cat extends Animal {
14 makeSound() {
15 console.log("meow");
16 }
17}
18
19// Error
20class DogCat extends Dog, Cat {
21 makeSound() {
22 super.makeSound(); // ??
23 }
24}
상속 관계가 다음과 같은 구조이기 때문에 다이아몬드 문제라고 부릅니다
DogCat
클래스가 Dog
와 Cat
클래스를 상속받을 수 있다면 super.makeSound()
호출 시 어떤 클래스의 makeSound
메서드를 호출해야 할지 알 수 없습니다.interface
를 통해 구현(implements
)하는 방식으로 다중 상속을 대체합니다.1interface Animal {
2 makeSound(): void;
3}
4
5interface Movable {
6 move(): void;
7}
8
9class Dog implements Animal, Movable {
10 makeSound() {
11 console.log("bow wow");
12 }
13
14 move() {
15 console.log("running...");
16 }
17}
1class Animal {
2 // 어디서든 접근 가능
3 public name: string;
4 // 클래스 내부와 상속받은 클래스에서만 접근 가능
5 protected age: number;
6 // 클래스 내부에서만 접근 가능
7 private weight: number;
8}
Interface
: 클래스에서 공통된 메서드와 속성을 정의할 때 사용, implements
를 통해 다중 상속을 대체Abstract Class
: 클래스에서 공통된 메서드와 속성을 정의할 깨 사용, 구현을 재공하고 하위 클래스에서 구현을 확장할 때 사용, 코드의 중복을 줄이고 상속을 통해 계층 구조를 만들 때 사용implements
를 통해 다중 상속을 대체할 수 있고 추상 클래스는 기본적인 구현을 재공할 수 있으며 하위 클래스에서 구현을 확장할 수 있습니다.
어느 것이 더 좋다고 말하기 보단 상황에 맞게 사용하는 것이 중요합니다.