제네릭(Generic)이란?
자바에서 제네릭(generic)이란 데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미한다.
클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법
제네릭 사용 이전 문제점
아무 데이터를 담을 수 있는 SimpleBox 클래스가 있다하자.
public class SimpleBox {
private Object data;
public Object getData() { return data; }
public void setData(Object data) { this.data = data; }
}
Java
복사
SimpleBox 클래스에 문자열을 하나 넣고 사용하는 코드를 작성해보자
SimpleBox simpleBox = new SimpleBox();
simpleBox.setData("내 문자열");
Java
복사
"내 문자열" 이라는 데이터를 넣은 박스를 사용하는 코드를 작성해보자
Integer data = (Integer) simpleBox.getData();
int i = data + 1;
System.out.println(i);
Java
복사
위 코드에서 String 이 들어가 있는 simpleBox 를 (Integer) 로 형변환 했음에도, 컴파일에는 문제가 없다. SimpleBox 클래스는 아무 타입이나 받을 수 있는 Obejct 가 있기 때문.
실행 시에 문제가 발생한다.
이러한 문제가 발생하는 것을 막으면서도, 어떤 데이터 타입이든 받을 수 있게 하는 코드가 필요하다.
이게 제네릭 문법이 만들어진 이유.
제네릭을 사용한 코드
박스를 만들되, <T> 를 이용한다.
여기서 T 는 타입 파라미터(Type Parameter)이다.
public class GenericBox<T> {
private T data;
public T getData() { return data; }
public void setData(T data) { this.data = data; }
}
Java
복사
GenericBox<String> genericBox = new GenericBox<>();
genericBox.setData("내 문자열");
Java
복사
위 코드에서는 <> 안에 String 을 넣어줌으로, String 타입만 받는다고 선언했다.
Integer Double 등등 모든 타입이 가능하다.
제네릭 사용 이전처럼 Box 를 사용하는 코드를 작성하자
Integer data = (Integer) genericBox.getData();
int i = data + 1;
System.out.println(i);
Java
복사
위 코드는 컴파일에서부터 에러가 난다.
String 타입을 받기로 했는데 Integer 가 들어오려 하기 때문.
이처럼 잘못된 타입이 들어오는 것을 막으면서, 코드의 재사용이 가능하게 됐다.
제네릭 클래스의 타입 인자 제한
제네릭 클래스 선언 시, 입력받는 타입 종류를 더 제한할 수 있다.
위 코드에서 평균을 구하는 기능을 GenericBox 클래스에 넣어보자
public class GenericBox<T> {
private T data;
public T getData() { return data; }
public void setData(T data) { this.data = data; }
return ((Double)this.data + (Double)a.getData()) / 2;
}
Java
복사
자기 data 를 double 로 강제 형변환 하고, 인자로 받은 a 도 double 로 강제 형변환 한 뒤 평균 값을 연산하는 메서드이다.
이제 이 기능을 사용해보자
NumberGenericBox<Double> box1 = new NumberGenericBox<>();
box1.setData(5.0);
NumberGenericBox<Double> box2 = new NumberGenericBox<>();
box2.setData(8.0);
System.out.println(box1.average(box2));
Java
복사
결과
6.5
Java
복사
double 형이 GenericBox 에 들어오게 되면 문제 없이 실행된다.
하지만 Integer 형이 들어오면 문제가 생긴다.
NumberGenericBox<Integer> box1 = new NumberGenericBox<>();
box1.setData(5);
NumberGenericBox<Integer> box2 = new NumberGenericBox<>();
box2.setData(8);
System.out.println(box1.average(box2));
Java
복사
컴파일은 되지만, ClassCastException 이 발생한다.
Integer 타입을 Double 타입으로 형변환 하는 것이 불가능 하기 때문.
이런 경우, Number 클래스의 하위 클래스만 받도록 설정할 수 있다.
public class NumberBoundedGenericBox<T extends Number> {
private T data;
public T getData() { return data; }
public void setData(T data) { this.data = data; }
public double average(NumberBoundedGenericBox<T> a) {
return (this.data.doubleValue() + a.getData().doubleValue()) / 2;
}
}
Java
복사
이제 NumberBoundedGenericBox 에서는 Number 의 하위클래스인 Integer Double 등의 클래스만 사용할 수 있다.
doubleValue 는 Number 클래스가 제공하는 메서드이다.
받은 값을 primitive date type 으로 바꿔준다.
이제 새로운 박스를 사용하자
NumberBoundedGenericBox<Integer> b1 = new NumberBoundedGenericBox<>();
b1.setData(5);
NumberBoundedGenericBox<Integer> b2 = new NumberBoundedGenericBox<>();
b2.setData(8);
System.out.println(b1.average(b2));
Java
복사
이제 정상적으로 작동한다.