오버로딩과 오버라이딩

권태형·2023년 4월 5일
0

지식정리

목록 보기
52/72
post-thumbnail

😀IT용어 정리에서 간단하게 정의만 작성한 오버로딩(OverLoading)과 오버라이딩(OverRiding)에 대해서 예시를 포함해 좀 더 자세하게 알아보도록 하자

오버로딩(OverLoading)이란?

오버로딩이란 같은 이름의 메소드를 여러 개 정의하는 것이다.

Overload의 사전적 의미는 v.과적하다 즉, 너무 많이 싣는 것을 말한다. 사전적 의미와 같이 하나의 생성자 또는 메소드 이름으로 여러가지 기능을 담는다는 의미에서 붙여진 이름이라고 생각할 수 있다.

오버로딩된 메소드들은 파라미터의 수, 타입, 순서 등이 다르지만 같은 이름을 가지고 있다.

반대로 말하면, 같은 이름을 가졌으나 파라미터의 수, 타입, 순서 등에서 반드시 하나라도 달라야 오버로딩이 성립한다고 할 수 있다.

😀필자가 개발공부를 할 때 배운 Javascript에서는 오버로딩을 지원하지 않는다. 계층구조로 작성하여 controller, service, repository에 같은 이름의 메소드를 사용해도 실제 들어가는 파라미터의 수, 타입, 순서 등은 고정되어 있기에 같은 이름이라도 오버로딩이라고 볼 수 없다.

Javascript가 아닌 Typescript에서는 오버로딩을 지원한다. Javascript는 동적 타입의 언어이기 때문에 함수 호출 시 인자의 개수, 타입 등을 체크하지 않기 때문에 컴파일시점에서 오버로딩을 구현할 수 없다. 하지만 Typescript는 Javascript에서 지원하지 않는 기능을 추가하면서 개발되어, 정적인 특징을 가지고 오버로딩을 구현할 수 있도록 개발되었다.


오버로딩의 예시코드

😀이제 Typescript에서의 오버로딩에 대해서 구체적인 예시를 보면서 알아보자.

예시를 바로 들기전에 오버로딩의 성립조건에 대해 다시 한번 짚어보자.
"같은 이름의 메소드를 여러 개 정의하되 파라미터의 수, 타입, 순서 등이 달라야 한다."라고 축약할 수 있다.

아주 간단한 예시를 들어보자
(아래의 예시코드들은 하나의 클래스 내부에서 동작한다고 가정한다.)

function add(a: number, b: number): number; //시그니처
function add(a: string, b: string): string;
function add(a: any, b: any): any {
  return a + b;
}

console.log(add(1, 2)); // 3
console.log(add("Hello, ", "TypeScript")); // Hello, TypeScript

매개변수 타입이 다른 두 개의 시그니처를 정의한 후에는, 매개변수의 타입에 따라 해당 시그니처에 맞게 자동으로 함수를 호출할 수 있다. 하지만 any타입으로 함수를 정의하게 되면, 타입에 명확성이 떨어지고 안전한 타입을 전달하지 못할 수 있다.

😀같은 함수를 any타입이 아닌 unknown타입과 명시적인 타입으로 구연해보자

//unkown타입으로 작성
function add(a: number, b: number): number;
function add(a: string, b: string): string;
//매개변수를 unknown타입으로 지정, 반환값을 유니온 타입으로 보정
function add(a: unknown, b: unknown): number | string {
  if (typeof a === 'number' && typeof b === 'number') {
    return a + b;
  } else if (typeof a === 'string' && typeof b === 'string') {
    return a.concat(b);
  } else {
    throw new Error('Unsupported types');
  }
}

console.log(add(1, 2)); // 3
console.log(add("Hello, ", "TypeScript")); // Hello, TypeScript

//-----------------------------------------------------------------------------
//명시적인 타입으로 작성
function add(a: number, b: number): number;
function add(a: string, b: string): string;

//유니온 타입으로 매개변수와 반환결과의 타입을 보정
function add(a: number | string, b: number | string): number | string {
  if (typeof a === 'number' && typeof b === 'number') {
    return a + b;
  } else if (typeof a === 'string' && typeof b === 'string') {
    return a.concat(b);
  } else {
    throw new Error('Unsupported types');
  }
}

console.log(add(1, 2)); // 3
console.log(add("Hello, ", "TypeScript")); // Hello, TypeScript

이처럼 add의 함수이름에 각 매개변수가 number가 되었을 때, string되었을 때 동일한 이름에 매개 변수만 다른 여러 버전의 함수를 만드는 것을 오버로딩이라 할 수 있다.

😀조금 더 어려운 복잡해 보이는 예시코드를 확인해 보자
아래와 같이 받는 첫번째 매개변수가 다른 비슷한 로직의 함수가 여러개 있을 때 코드의 중복을 줄이고 재사용성을 높이기 위해 오버로딩을 사용해 함수를 구현할 수 있다. 스크롤을 예시까지만 내려서 보고 직접 오버로딩이 되는 코드를 구연해 보자

const addZero = (num: number) => (num > 9 ? "" : "0") + num;
 
function formatDate(date: Date, format = "yyyyMMdd"): string {
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}
 
function formatDateString(dateStr: string, format = "yyyyMMdd"): string {
    const date = new Date(dateStr);
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}
 
function formatDateTime(datetime: number, format = "yyyyMMdd"): string {
    const date = new Date(datetime);
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}

위 코드를 오버로딩을 사용해 리팩토링

const addZero = (num: number) => (num > 9 ? '' : "0") + num;
function formatDate(date: Date, format = "yyyyMMdd"): string;
function formatDate(date: number, format = "yyyyMMdd"): string;
function formatDate(date: string, format = "yyyyMMdd"): string;
 
// 위 3개 함수를 함수 오버로딩 처리, date는 유니온 타입사용
function formatDate(date: string | Date | number, format = "yyyyMMdd"): string {
    const dateToFormat = new Date(date);
    // … dateToFormat validation … 만약 empty나 1, 0이 들어왔을 때 validation 처리가 필요
    const yyyy = dateToFormat.getFullYear().toString();
    const MM = addZero(dateToFormat.getMonth() + 1);
    const dd = addZero(dateToFormat.getDate());
 
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}

오버라이딩(OverRiding)이란?

상위 클래스에서 정의된 메소드를 하위 클래스에서 재정의하는 것

하위 클래스에서 상위 클래스와 동일한 이름, 파라미터 리스트, 반환 타입을 가지는 메소드를 재정의하는 것이다.

상속받은 메소드를 그대로 사용할 수도 있지만, 자식 클래스에서 상황에 맞게 변경해야하는 경우 오버라이딩할 필요가 생긴다.

부모 클래스에서 오버라이딩을 당하는(?) 메소드를 오버라이든 메소드 라고 하고, 자식 클래스에서 오버라이딩된 메소드를 오버라이딩 메소드라고 했을 때, 오버라이딩이 이루어지기 위해서는 다음 두 가지의 조건을 만족해야 한다.

  1. 오버라이든 메소드의 매개변수 타입오버라이딩 메소드의 매개변수 타입과 💯같거나 상위 타입이어야 한다. (단, 오버라이딩 메서드의 매개변수 타입이 Any 이면 예외)

  2. 오버라이든 메소드의 매개변수 개수오버라이딩 메소드의 매개변수 갯수와 💯같거나 많아야 한다. (단, 조건 1이 성립하는 전제가 있어야 함)

오버라이딩 예시코드

😀오버라이딩도 간단한 예시코드를 들어보자.

class Animal {
  makeSound(): void {
    console.log("동물이 소리를 냅니다.");
  }
}

class Dog extends Animal {
  makeSound(): void {
    console.log("멍멍");
  }
}

const animal: Animal = new Animal();
const dog: Dog = new Dog();

animal.makeSound(); // "동물이 소리를 냅니다."
dog.makeSound(); // "멍멍"

위와같이 같은 메소드(makeSound())를 자식 클래스인 Dog클래스가 Animal클래스에게 상속받아 Dog클래스에서 makeSound()메소드를 재정의 하는 것을 오버라이딩이라고 부른다.

😀이번에도 조금 더 복잡해보이는 예시코드를 수정해보자

class Bird {
    constructor() { }
    flight(kmDistance: number = 0, kmSpeed: number = 0): void {
    	console.log(`새가 ${kmDistance}km를 ${kmSpeed}km의 속도로 비행했습니다.`);
    }
}

let bird = new Bird();
bird.flight(1000, 100); // 새가 1000km를 100km의 속도로 비행했습니다.
  • 문제1. 위의 코드의 class Bird를 상속받는 Eagle클래스를 작성하고,
    flight()메소드를 재정의 해서 eagle.flight(1000)의 출력이
    독수리가 1000km를 비행했습니다.가 나올 수 있게 오버라이딩을 이용해 코드를 추가 해보자
class Bird {
    constructor() { }
    flight(kmDistance: number = 0, kmSpeed: number = 0): void {
    	console.log(`새가 ${kmDistance}km를 ${kmSpeed}km의 속도로 비행했습니다.`);
    }
}

class Eagle extends Bird {
    constructor() {
    	super();
    }
    
    flight(kmDistance2: number = 0): void {
    	console.log(`독수리가 ${kmDistance2}km를 비행했습니다.`)
    }
}

let bird = new Bird();
bird.flight(1000, 100); // 새가 1000km를 100km의 속도로 비행했습니다.

let eagle = new Eagle();
eagle.flight(); // 독수리가 0km를 비행했습니다.
eagle.flight(1000); // 독수리가 1000km를 비행했습니다.

참고자료(출처)
티스토리 heyhyo 블로그 포스팅 [Java]오버로딩 & 오버라이딩(Overloading & Overriding)
티스토리 기록의힘 블로그 포스팅 [TypeScript] 타입스크립트 함수 오버로딩 : Function Overloading
티스토리 김진권 블로그 포스팅 [typescript] 11. 오버라이딩과 오버로딩 그리고 추상 class

profile
22년 12월 개발을 시작한 신입 개발자 ‘권태형’입니다. 포스팅 하나하나 내가 다시보기 위해 쓰는 것이지만, 다른 분들에게도 도움이 되었으면 좋겠습니다. 💯컬러폰트가 잘 안보이실 경우 🌙다크모드를 이용해주세요.😀 지적과 참견은 언제나 환영합니다. 많은 댓글 부탁드립니다.

0개의 댓글