let result: number = rectangle.getArea(); console.log(result); // 4
상속관계, 포함관계
클래스 간의 관계는 상속 관계와 포함 관계가 있다.
상속 관계
부모 클래스를 기반 클래스 또는 슈퍼 클래스라고 하며 이를 상속 받는 자식 클래스를 파생 클래스 또는 서브 클래스라고 부른다. 자식 클래스는 부모 클래스에 공개된 메서드나 변수를 상속받는다.( IS-A관계) 상속을 위해 extends 키워드를 지원한다. 단일 상속만 지원하므로 자식 클래스는 하나의 부모 클래스만 상속받을 수 있다. 상속시 자식 클래스 생성자에서 super() 메서드를 호출해 부모 클래스의 생성자를 호출해주어야한다.
1 2 3 4 5
class <자식 클래스> extends <부모 클래스>{ constructor(){ supper(); }; }
포함 관계
클래스가 다른 클래스를 포함하는 (HAS-A) 관계이다. 합성(composition)관계 집합(aggregation)관계
합성 관계는 전체가 부분을 포함하며 강한 관계이다.
1 2 3 4 5 6 7 8 9 10 11
class Engine {}
class Car { private engine; constructor() { this.engine = new Engine(); } }
let car = new Car(); car = null;
Car 클래스에 선언된 engine 객체는 Car 클래스가 new로 생성될때 함께 생성되고, car가 null이되면 함께 제거된다.(생명주기를 함께한다)
집합 관계는 전체가 부분을 포함하며 약한 관계이다.
1 2 3 4 5 6 7 8 9 10 11
class Engine {}
class Car { private engine: Engine; constructor(engine: Engine) { this.engine = engine; } }
let engine = new Engine(); let car = new Car(engine);
Car 클래스의 car 객체가생성될때 외부에서 생성된 engine 객체를 전달하고 있다.따라서 car가 null이 되더라도, engine 객체는 Car클래스 외부에서 선언되어 null이 되지 않아 생명주기를 함꼐 하지 않는다.
let account = new Account(); console.log( `1번) 적금 : ${account.balance}원, ${account.getBalance}원 / 이율 : ${ account.interestRate }, ${account.getInterestRate()}% ` );
let myAccount = new MyAccount();
account.bankName; // Property 'bankName' is protected and only accessible within class 'Account' and its subclasses
접근 제한자를 생략하면 기본적으로 public이 된다. 위 예제에서 defaultBalance는 생성자 내부에서만 사용 가능한 private가 된다. bankName은 protected로 자식 클래스에서 접근가능하지만 객체를 통한 외부접근은 불가능하다.(account.bankName 불가) interestRate는 readOnly로 자식 클래스에서도 접근 가능하고 외부 접근도 가능하다.
추상 클래스
추상 클래스는 구현 메서드와 추상 클래스가 동시에 존재할 수 있다. abstract 키워드를 클래스 선언 앞에 붙여서 선언한다.
// Bird : 오버라이든 클래스 (부모) class Bird{ // 오버라이든 메서드 flight(sppeed : any = 0){} // 부모의 메서드의 타입이 같거나 상위 타입이어야함, 갯수가 같거나 많아야함 }
// Eagle : 오버라이딩 클래스 (자식) class Eagle extends Bird{ // 오버라이딩 메서드(부모 메서드와 이름이 같음) flight(sppeed2 : number = 0){} // 매개변수 이름은 달라도 된다. 단 타입은 같거나 하위 타입이여함 }
위 두개의 조건을 만족하지 않으면 오버라이딩 되지 않음
1 2 3
// 위 경우는 메서드의 이름이 같아도 오버라이딩 되지 않는다. filght(speed : any = 0; distance : number = 0); // 오버라이든 메서드 filght(speed : number = 0; distance : string = ''); // 오버라이딩 메서드
오버로딩(overloading)
메서드 오버로딩은 메서드의 이름이 같지만 매개변수 타입과 개수를 다르게 정의하는 방법을 말한다.
오버라이딩 메서드를 오버로딩하는법
부모 클래스에 상위 타입을 가지는 오버라이든 메서드를 구현하고, 파생 클래스에서 오버라이딩 메서드를 선언해 구현함
let unionTypeChecker = new UnionTypeChecker(); unionTypeChecker.typeCheck(123); unionTypeChecker.typeCheck("happy"); // unionTypeChecker.typeCheck(true); // 에러
위 예제는 any 타입에 number와 string 만 받을 수 있도록 typeCheck 메서드를 정의함 any 타입이 모든 타입을 받을 수 있을 것 같지만 실제로는 number와 string 만 받을 수 있다.
인터페이스를 클래스에서 구현하여 오버로딩
인터페이스를 이용해 오버로딩 하라면 인터페이스에 오버로딩할 기본 메서드를 선언하고, 클래스에서 기본 메서드를 구현해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
interface IPoint { getX(x: any): any; // 기본 메서드 선언 } class Point implements IPoint { getX(x?: number | string): any { // ?매개변수에 ? 을 추가해 선택 매개변수가 되어 입력값이 없는 호출을 받을 수 있다. p.getX(); if (typeof x === "number") { return x; } elseif (typeof x === "string") { return x; } } }
let p = new Point(); console.log(p.getX()); // undefined console.log(p.getX("hello")); // hello console.log(p.getX(123)); // 123
다형성
여러 타입을 받아들여 여러 형태를 가지는 것. 타입스크립트에서는 다음 세 가지가 대표적인 예이다.
1) 부모 클래스(Planet) 타입으로 지정된 객체 참조변수(earth)는 자식 클래스의 객체(new Earth)를 할당받더라도 실제 동작은 부모 클래스를 기준으로 실행된다. 따라서 earth는 부모 메서드는 접근 가능하지만 자식 클래스의 매개변수(features)에는 접근 할 수 없다.
2) 그런데, stop()은 부모 클래스의 stop() 메서드가 자식 클래스로 오버라이딩 되어있다. 이럴 경우 자식 클래스의 메서드가 우선으로 호출된다. 즉 오버라이든 < 오버라이딩 이처럼, 런타임 시에 호출될 메서드가 결정되는 특성을 런타임 다형성이라고 한다.
getAge(): number { return10; } hasClub() { returntrue; } }
let man: IPerson = new MyPerson(); // 1) console.log(man.getAge()); // 2) console.log(man.hasClub()); //3) Property 'hasClub' does not exist on type 'IPerson'.
1) new MyPerson()는 원래 MyPerson 타입이지만 객체 참조변수(man)에 할당되면서 인터페이스(IPerson) 기준으로 접근이 이뤄진다. 2) 따라서, man은 매개변수 age와 getAge()메서드에는 접근 가능하지만, 3) 구현 클래스(MyPerson)에 새롭게 추가된 hasClub() 메서드는 접근 할 수 없다.