[JAVA] 제네릭(Generic)이란?
제네릭(Generic)이란?
자바에서 ArrayList<Integer>와 같이 <> 꺽쇠 안에 클래스 타입이 명시되어있는 것을 확인할 수 있다. [그림 1]은 ArrayList의 내부이다. ArrayList<E>처럼 'E'라고 표시된 것을 확인할 수 있다.
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList<String> list2 = new ArrayList<String>();
위와 같이 ArrayList에 저장할 타입을 Integer, String 등으로 지정할 수 있다.
이처럼 데이터의 타입을 일반화(Generalize)하는 것을 제네릭(Generic)이라고 할 수 있다. 이러한 제네릭은 클래스나 메서드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정할 수 있다. 따라서 컴파일 시에 미리 타입 검사(Type Check)를 수행할 수 있다.
이러한 타입 검사를 컴파일 시에 수행하면 다음과 같은 장점을 얻을 수 있다.
1. 클래스나 메서드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있다.
2. 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있다.
3. 타입에 대해 유연성과 안정성을 확보한다.
4. 런타임 환경에 영향을 주지 않는 전처리 기술이다.
JDK 1.5 이전에서는 여러 타입을 사용하는 대부분의 클래스나 메서드에서 인수나 반환 값으로 Object 타입을 사용했다.
List v = new ArrayList();
v.add("test"); // A String that cannot be cast to an Integer
Integer i = (Integer)v.get(0); // Run time error
위와 같은 코드는 런 타임 에러를 발생시킨다. List에 String을 저장해놓고 Integer로 타입 변환하는 오류가 런타임에 잡히는 것이다.
List<String> v = new ArrayList<String>();
v.add("test");
Integer i = (Integer)v.get(0); // (type error) compilation-time error
하지만 위와 같이 제네릭을 이용하면 컴파일 에러를 발생시켜 컴파일 시에 오류를 잡아낼 수 있다. 런 타임 에러보다 컴파일 에러가 훨씬 디버깅이 쉽기 때문에 이점이 많다.
또한, Object 객체를 이용하면 다시 원하는 타입으로 타입 변환을 해야 하기 때문에 이에 따른 성능 저하, 오류 발생 가능성 등이 존재한다. 따라서 제네릭을 사용하면 컴파일 시에 타입이 정해 지므로, 타입 검사나 타입 변환과 같은 작업을 생략할 수 있게 된다.
class Sample<T>{
// 데이터 타입 T
private T data;
// 파라미터로 T를 받는다
public void setData(T data){
this.data = data;
}
// return 타입 : T
public T getData(){
return data;
}
}
제네릭을 이용하여 위와 같이 제네릭 클래스와 메서드를 선언할 수 있다. 'T'를 타입 변수라고 하며, 임의의 참조형 타입을 의미한다. 'T'는 예시일 뿐 다른 문자를 사용할 수도 있다.
위와 같이 선언된 제네릭 클래스를 생성할 때는 타입 변수 자리에 사용할 실제 타입을 명시하면 된다.
Sample<Integer> sample = new Sample<Integer>();
sample.setData(2);
Integer ret = sample.getData();
이렇게 타입 변수를 지정하면, 'T' 자리에 Integer가 들어간다고 생각하면 이해하기 편하다.