제네릭 클래스(Generic Class)란?
Generic Class는 직역하면 포괄적인 클래스입니다. 여기서 포괄적이라는 의미는 특정 타입만을 다루지 않고 여러 종류의 타입을 일반화시켜 표현함을 나타냅니다. 즉 Integer만 다루는 계산기가 아니라 Integer, Double 등 숫자와 관련한 Wrapper Class 들도 사용할 수 있도록 일반화한 계산기를 만들고자 하는 것이죠. 그렇지 않으면 모든 Number 클래스에 대한 계산기 클래스를 일일이 하나하나 만들어줘야 할테니까요.
제네릭 클래스 예제 - 탈 것과 회사
간단한 예제를 통해 제네릭 클래스를 보여드리겠습니다. 여기 탈 것(Vehicle) 클래스가 있습니다. 그리고 탈 것 종류(Truck, SUV, Bicycle)에 따라 회사를 만들고자 합니다. 근데 세 종류에 대해 회사를 만들려면 세 종류의 클래스를 제작해야 합니다. 아래와 같이 말이죠.
class Vehicle {
double height;
double width;
String name;
}
class Truck extends Vehicle {
double boardWeight;
}
class SUV extends Vehicle {
int passenger;
}
class Bicycle extends Vehicle {
String gearCompany;
}
class TruckCompany{
Truck product;
}
class SUVCompany{
SUV product;
}
class BicycleCompany{
Bicycle product;
}
이렇게 되면 탈 것의 종류가 50개면 회사 클래스도 50개를 만들어야 합니다. 또 만약 변경점이 생기면 그 모든 클래스의 내용을 일일이 다 바꿔야 하죠. 개발자가 가장 싫어하는 상황이 아닐 수 없습니다. 그럼 제네릭 클래스는 이 부분을 어떻게 해결해줄까요?
우리 무슨 회사인가요? <T> 를 만드는 회사라네
저 세 종류의 차를 담기 위해선 어떤 특정한 클래스로 지정해선 안됩니다. 근데.. 지정을 하지 않으면 다양한 오류가 생기지 않을까요? 따라서 제네릭 클래스는 T라는 타입 매개변수를 통해 자유롭게 지정할 수 있도록 합니다. 클래스를 정의할 때 클래스명 오른편에 <> 꺽새 괄호안에 제시되는 것으로 어떤 타입을 클래스에서 사용할 것인지 명시해주는 것이죠.
package generic;
public class VehicleGenericEx {
public static void main(String[] args) {
// 각 탈 것에 맞는 회사 생성
Company<Truck> tc = new Company<Truck>();
Company<SUV> sc = new Company<SUV>();
Company<Bicycle> bc = new Company<Bicycle>();
// 제품에 들어갈 인스턴스 생성
Truck monster = new Truck();
SUV windforce = new SUV();
// 인스턴스 대입
sc.product = windforce;
tc.product = monster;
// 출력 예시
System.out.println("" + tc.product.name + " " + sc.product.height);
}
}
class Vehicle {
double height;
double width;
String name;
Vehicle() {
height = 0;
width = 0;
name = "Unknown";
}
}
class Truck extends Vehicle {
double boardWeight;
Truck() {
boardWeight = 0;
}
}
class SUV extends Vehicle {
int passenger;
SUV() {
passenger = 4;
}
}
class Bicycle extends Vehicle {
String gearCompany;
Bicycle() {
gearCompany = "Unknown";
}
}
// 타입 파라미터(매개변수)를 이용한 제네릭 클래스
class Company<T> {
T product;
}
근데... 이렇게 만들면 Integer도 제품이 될 수 있습니다. 왜냐하면 타입 파라미터에 아무런 제약 사항도 걸어놓지 않았으니 어떤 클래스도 올 수 있기 때문이죠. 따라서 타입의 범위를 축소시킬 필요가 있습니다. 이때 등장하는 것이 extends 키워드입니다.
우리가 클래스를 생성할 때 extends는 어떤 부모 클래스로부터 물려받는다는 의미였습니다. 여기서도 같은 의미입니다. T라는 타입 파라미터가 어떤 클래스의 자식인지 명시해주는 역할입니다.
class Company<T extends Vehicle> {
T product;
}
이렇게 선언하면 Vehicle 클래스를 상속받은 3가지 클래스만 타입 파라미터로 사용될 수 있습니다. 범위가 축소되는만큼 클래스 안에서 사용할 수 있는 필드와 메소드가 생겨나게 됩니다. 만약 Vehicle의 높이를 조정하고 싶다면 product.height 를 조정하는 것처럼요. 주의하실 점은 Vehicle 클래스 자체도 타입 파라미터가 될 수 있다는 점입니다.