면접에서 객체지향언어에서의 다형성에대해 코드를 짜보라는 질문을 받았다.
분명 오버로딩 오버라이딩 관련한 얘기인데 한동안 자바스크립트로 함수형 코딩만 하다보니 객체 작성방법도 잘 기억이 안나고 내 기억엔 오버로딩은 지원하지 않아서 결국엔 오버라이딩만 typeScript interface로 구현했다...
생각해보면 자바스크립트로 객체지향 언어인데 모르는게 잘못된거라고 생각한다.
그래서 자스랑 타스로 오버로딩이란 오버라이딩 구현법을 공부해봤다.
Overload의 사전적 의미는 과적하다 즉, 너무 많이 싣는 것을 말한다. 객체지향 언어에서 오버로딩은 같은 이름의 생성자 또는 메소드를 여러 개 선언하는 것을 말한다. 'overload' 의 뜻처럼 하나의 생성자 또는 메소드 이름으로 여러가지 기능을 담는다는 의미에서 붙여진 이름이라고 생각할 수 있다.
자바스크립트는 기본적으로 오버로딩을 지원하지 않는다
아마 타입이 없어서 그런것으로 생각한다
따라서 arguments를 사용해서 구현한다고 한다.
📌 arguments는 자바스크립트에서 인수가 전달되면 그 인수들을 배열로 저장하는 객체이다. (자바스크립트는 매개변수의 개수를 맞추지 않아도 오류 안난다)
function add() {
if (arguments.length === 2 && typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
return arguments[0] + arguments[1];
} else if (arguments.length === 2 && typeof arguments[0] === 'string' && typeof arguments[1] === 'string') {
return arguments[0].concat(arguments[1]);
} else {
throw new Error('타입이 맞지 않습니다.');
}
}
위처럼 aruments 객체를 활용해서 전달반은 인수들을의 타입을 체크해서 로직을 정의해줘야 한다.
근데 사실 오버로딩을 사용한다는건 매개변수를 다 알고 작성한다는 의미이다.
자바스크립트에서 인수를 전달해 주지 않으면 그 매개변수는 그냥 undefined
가 되기 때문에
이 이상한 유연성을 사용해서 가장 최대로 매개변수를 선언해 주고 그 안에서 아래와 같이 선언해 주면 될것같다.
function add(a, b, c) {
if (typeof a === 'number' && typeof b === 'number' && c === undefind) {
return arguments[0] + arguments[1];
} else if typeof a === 'string' && typeof b === 'string' && c === undefind) {
return arguments[0].concat(arguments[1]);
} else {
throw new Error('c가 들어있네요');
}
}
Java
public class Example {
public int add(int a, int b) {
return a + b;
}
public String add(String a, String b) {
return a.concat(b);
}
public static void main(String[] args) {
...
}
}
자바랑 비교해보면 확실히 자바가 깔끔하고 가독성이 좋다.
그럼 타입스크립트는 조금 나은가?
타입스크립트는 오버로딩을 지원한다.
따라서 아래와 같이 코드를 작성하면 오버로딩을 구현할 수 있다.
사실 타입을 적어주는 거 말고는 크게 차이는 없지만 타입스크립트 답게
안전성을 보장하기 위해 선언과 구현을 모두 해주어야 한다.
class Example {
add(a: number, b: number): number;
add(a: string, b: string): string;
add(a: string, b: string, c: string): string;
add(a: number | string, b: number | string, c?: 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 parameter types');
}
}
}
타입스크립트는 자바스크립트처럼 arguments를 사용하지 않기 때문에 아래와 같이 구현하면 비슷할 것 같다.
class Example {
add(...args: any[]) {
if (args.length === 2 && typeof args[0] === 'number' && typeof args[1] === 'number') {
return args[0] + args[1];
} else if (args.length === 2 && typeof args[0] === 'string' && typeof args[1] === 'string') {
return args[0].concat(args[1]);
} else {
throw new Error('타입이 맞지 않습니다.');
}
}
}
그래도 타입스크립트에서 하라는대로 첫번째처럼 안전하게 작성하는게 나을것 같다.
Override의 사전적 의미는 우선하다, 중단시키다 등이다. 객체지향 언어에서 오버라이딩은 부모클래스로부터 상속받은 메소드를 재정의하는 것을 말한다. 오버라이딩된 메소드는 부모 클래스의 원래 메소드보다 우선되기 때문에 자식 객체에서 메소드 호출 시 오버라이딩 된 메소드가 호출된다.
다행히 자바스크립트는 오버라이딩을 지원한다.
아래처럼 자바스크립트의 프로토타입 문법을 활용해서 오버라이딩을 구현할 수 있다.
me 인스턴스는 Person의 프로토타입을 가리키는데
me에 같은 이름의 프로퍼티나 메서드를 정의하게 되면 프로토타입 체인의 가장 앞에 me가 있기 때문에 me의 프로퍼티나 메서드를 사용하게 된다.
이를 통해 함수형으로도 오버라이딩을 사용할 수 있다.
const Person = (function (){
function Person(name) {
this.name = name;
}
Person.prototype.say = function(){
console.log('hi')
}
return Person
}());
const me = new Person('kim');
me.say = function () {
console.log('me hi')
}
자바스크립트에서도 class 문법이 생겼기 때문에 굳이 함수형으로 쓸필요 없이 자바와 비슷하게 작성하면 오버라이딩을 구현할 수 있다.
class Person {
say(){
console.log('hi')
}
}
class Baby extends Person {
constructor(){
super()
}
say(){
console.log('응애')
}
}
타입스크립트도 class 문법이 있기 때문에 위처럼 구현하면 된다
타입스크립트에는 interface가 있기 때문에 interface 오버라이딩을 한번 보면
interface Person{
name:string;
say:()=>void;
}
interface Baby extends Person{
say:()=>string;
}
const person:Person= {
name:'kim',
say:()=>{console.log(123)}
}
const baby:Baby= {
name:'kim',
say:()=>{console.log(123);}
}
이렇게 인터페이스를 상속받고 나서 같은 이름의 메서드의 타입을 정해주게 되면 자식 요소의 타입을 사용하게 된다.
따라서 위 코드의 baby는 오류가 나게 된다.
이렇게 자바스크립트도 객체지향 언어이기 때문에 지원하지 않아도 오버로딩 오버라이딩을 사용할 수 있다.
자바스크립트, 타입스크립트 둘다 공부를 열심히 해야 겠다..
참고: https://www.zerocho.com/category/JavaScript/post/59c17a58f40d2800197c65d6