#26 제네릭 타입을 애용하자
이 클래스는 Object를 기반으로 한 Stack 클래스입니다.
pop 메서드를 통해 꺼낸 Object 객체를 캐스팅해야 하는데 이 과정에서 런타임 에러가 발생할 수 있습니다.
이러한 클래스들이 제네릭화(generification)의 우선 대상입니다.
제네릭화 : 제네릭 타입을 이용할 수 있도록 개선하는 것
제네릭화에 대해서 알아보겠습니다.
Object를 기반으로 한 Stack 클래스를 일단 제네릭화한 GenericStack 클래스입니다.
클래스를 제네릭화 하기 위한 여러 단계를 살펴보겠습니다.
#01 클래스 선언부에 하나 이상의 타입 매개변수를 추가
수정 전 : public class Stack
수정 후 : public class GenericStack<E>
#02 모든 Object 타입을 타입 매개변수로 변경 후 컴파일
수정 전 : private Object[] elements
수정 후 : private E[] elements
E[] 배열을 만드는 과정에서 아래와 같이 컴파일 에러가 발생합니다.
이전 글에서 설명했듯이, 배열(구체화)과 E(비 구체화) 간에는 타입 안정성 측면에서 문제가 발생할 수 있기 때문에 같이 이용할 수 없습니다.
구체화 : 컴파일 시보다 런타임 시에 더 많은 정보를 가지고 있음
비 구체화 : 런타임 시보다 컴파일 시에 더 많은 정보를 가지고 있음
이를 해결하기 위한 두 가지 방법이 있습니다.
1. 생성자에서 Object 배열을 생성하고, 이를 제네릭 타입으로 캐스팅
이 과정에서 당연히 ClassCastException 경고가 뜨는데, 캐스팅이 안전하다고 밝혀지면 @SuppressWarnings 주석을 통해 해결할 수 있습니다.
2. Object 배열을 선언하고, 이를 제네릭 타입으로 캐스팅 반환
이 과정에서도 ClassCastException 경고가 뜨는데, 캐스팅이 안전하다고 밝혀지면 @SuppressWarnings 주석을 통해 해결할 수 있습니다.
두 가지 방법은 Object 배열을 언제 선언, 생성하고 또 이를 Generic 배열로 언제 캐스팅하는지에 따른 차이점이 있습니다.
둘 중 어떤 방법을 이용할지는 상황이나 취향에 달려있는데, 첫 번째 방법은 한 번만 제네릭 타입으로 캐스팅하면 되기 때문에 좀 더 보편적으로 이용되고 있습니다.
GenericStack 클래스를 이용하는 코드입니다.
String 요소들을 Stack 내부에 push 하고, 이를 다시 pop 해서 대문자로 변환할 때 별도의 명시적인 캐스팅 과정이 필요 없습니다.
컴파일러에서 자동으로 생성한 캐스트 코드가 이를 대신해주기 때문입니다.
이처럼 제네릭 타입은 별도의 캐스팅 과정이 필요한 코드보다 더 안전하고 편리합니다.
따라서 새로운 타입을 설계할 때 캐스팅 없이 이용할 수 있다면 제네릭하게 만드는 게 더 낫습니다.