제네릭(Generic)
제네릭은 Java, TypeScript 등의 언어에서 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징입니다. 특히, 한가지 타입보다 여러 가지 타입에서 동작하는 컴포넌트를 생성하는데 사용됩니다.
제네릭의 사전적 정의는 위와 같고, 실질적으로 어떻게 사용 할 수 있는지 바로 아래에서 알아보도록 합시다.
아래 포스트에서는 java, typeScript 두개의 언어로 예시를 들고, 각각 무엇을 의미하는지 정리해보고자 합니다.
제네릭 함수
제네릭(Generic) 함수는 제네릭 형식의 매개변수를 사용하여 선언된 함수입니다. 제네릭 함수를 호출하면, 실제로 넘겨진 argument의 형식이 사용되게 됩니다.
참고로, 제네릭타입으로 T, E, K 등 여러가지 대문자를 사용하는데, 이는 특별한 뜻이 있는 것이 아니기에.
원한다면 GENERICTYPE, ETYPE, 등 원하는 대로 다 사용할 수 있습니다.
하지만 하나의 대문자로 사용하는 것이 convention이고 알아보기도 쉬우니 convention을 따릅시다.
그럼, 아래 코드를 보시죠.
Java
// 1 2 3
public <T> T getFirstGeneric (T[] args){
return args[0];
}
위 함수를 보면, 1)T타입을 이용하는 제네릭 함수이며, 3)T타입이 들어있는 배열을 인자로 받아, 4)T타입을 리턴한 다는 것을 알수 있습니다.
설명은 아래와 같습니다.
- 제네릭 함수 선언 <T>: java에서 제네릭 함수를 선언하기 위해서는 접근제한자(e.g. public, private) 바로 뒤에 해당 제네릭 함수가 사용하는 제네릭 타입을 꺽쇠표시와 함께 적어, 해당 함수가 제네릭 함수임을 표시해줍니다.
- 리턴 타입 T: 일반적인 자바 문법처럼, 함수가 리턴하는 값을 의미합니다. 만약 String 타입을 리턴한다면, T가 아닌 String이 될 것입니다.
- 인자 타입 T[]: 매개변수의 타입을 의미합니다. 여기에서는 T타입이 들어있는 배열, args가 인자로 넘어 올 것을 알 수 있습니다.
TypeScript
타입스크립트의 경우도 흡사하나, 약간의 차이점이 있습니다.
// 1 2 3
function getFirstGeneric<T>(input: T[]): T {
return input[0]
}
위에서 예시로 든 자바와 같은 함수이지만, 문법의 차이로 위치가 조금씩 차이가 생깁니다.
- 제네릭함수 선언 <T>: 해당 함수가 T타입을 사용하는 제네릭 함수임을 알려줍니다. 만약, 두 개의 제네릭 타입을 사용하고 싶다면,
getFirstGeneric<T, K>와 같은 형식으로 사용합니다. - 인자 타입 T[]: 해당 함수가 T타입을 가진 배열을 인자로 받는 것을 의미합니다.
- 리턴 타입 T: 해당 함수가 T타입을 리턴하는 것을 알려줍니다.
제네릭 제약조건
제네릭을 사용한다고 하더라도, 우리는 사용하고싶은 몇가지 특정 타입이 있습니다.
예를 들어, 제네릭 함수안에서 length 메소드를 쓰고 싶다면, 배열이나 String 형식의 인자가 들어오는 것을 가정하고 있을 것입니다.
혹은 자바에서 특정 객체의 getMethod를 사용하고 싶다면 해당 객체타입의 인자가 들어올 것을 가정하고 있는 것이겠죠.
이런 경우, extends 를 이용하면 되는데 구체적으로 예시를 통해 어떻게 제네릭 함수를 작성 할 수 있는지 알아보도록 하겠습니다.
Java
public <T extends SomeClass> String getFirstGeneric (@RequestBody T[] args)
{
return args[0].getSomeField;
}
T타입을 someField라는 필드를 가지고 있는 SomeClass를 상속 받게 함으로써, SomeClass를 상속받지 않으면 에러가 발생하게 되며,
getSomeField 메소드도 잘 사용 할 수 있게 됩니다.
TypeScript
interface Lengthwise {
length: number;
}
function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
TypeScript도 똑같이 원하는 타입, interface를 extends로 상속받아 사용하면 원하는 method나 속성을 사용할 수 있게 됩니다.
참고링크: https://www.typescriptlang.org/ko/docs/handbook/2/generics.html